UndefinedBehaviorSanitizer¶
Introduction¶
UndefinedBehaviorSanitizer (UBSan) is a fast undefined behavior detector.UBSan modifies the program at compile-time to catch various kinds of undefinedbehavior during program execution, for example:
Array subscript out of bounds, where the bounds can be statically determined
Bitwise shifts that are out of bounds for their data type
Dereferencing misaligned or null pointers
Signed integer overflow
Conversion to, from, or between floating-point types which wouldoverflow the destination
See the full list of availablechecks below.
UBSan has an optional run-time library which provides better error reporting.The checks have small runtime cost and no impact on address space layout or ABI.
How to build¶
Build LLVM/Clang withCMake.
Usage¶
Useclang++ to compile and link your program with the-fsanitize=undefinedoption. Make sure to useclang++ (notld) as a linker, so that yourexecutable is linked with proper UBSan runtime libraries, unless all enabledchecks use trap mode. You can useclang instead ofclang++ if you’recompiling/linking C code.
%cattest.ccint main(int argc, char **argv) { int k = 0x7fffffff; k += argc; return 0;}%clang++-fsanitize=undefinedtest.cc%./a.outtest.cc:3:5: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
You can use-fsanitize=... and-fno-sanitize= to enable and disable onecheck or one check group. For an individual check, the last option that enablingor disabling it wins.
#Enableallchecksinthe"undefined"group,butdisable"alignment".%clang-fsanitize=undefined-fno-sanitize=alignmenta.c#Enablejust"alignment".%clang-fsanitize=alignmenta.c#Thesame.-fno-sanitize=undefinednullifiestheprevious-fsanitize=undefined.%clang-fsanitize=undefined-fno-sanitize=undefined-fsanitize=alignmenta.c
For most checks (checks), the instrumented program printsa verbose error report and continues execution upon a failed check.You can use the following options to change the error reporting behavior:
-fno-sanitize-recover=...: print a verbose error report and exit the program;-fsanitize-trap=...: execute a trap instruction (doesn’t require UBSanrun-time support). If the signal is not caught, the program will typicallyterminate due to aSIGILLorSIGTRAPsignal.
For example:
%clang++-fsanitize=signed-integer-overflow,null,alignment-fno-sanitize-recover=null-fsanitize-trap=alignmenta.cc
The program will continue execution after signed integer overflows, exit afterthe first invalid use of a null pointer, and trap after the first use of misalignedpointer.
%clang++-fsanitize=undefined-fsanitize-trap=alla.cc
All checks in the “undefined” group are put into trap mode. Since no checkneeds run-time support, the UBSan run-time library it not linked. Note thatsome other sanitizers also support trap mode and-fsanitize-trap=allenables trap mode for them.
%clang-fsanitize-trap=undefined-fsanitize-recover=alla.c
-fsanitize-trap= and-fsanitize-recover= are a no-op in the absence ofa-fsanitize= option. There is no unused command line option warning.
Available checks¶
Available checks are:
-fsanitize=alignment: Use of a misaligned pointer or creationof a misaligned reference. Also sanitizes assume_aligned-like attributes.
-fsanitize=bool: Load of aboolvalue which is neithertruenorfalse.
-fsanitize=builtin: Passing invalid values to compiler builtins.
-fsanitize=bounds: Out of bounds array indexing, in caseswhere the array bound can be statically determined. The check includes-fsanitize=array-boundsand-fsanitize=local-bounds. Note that-fsanitize=local-boundsis not included in-fsanitize=undefined.
-fsanitize=enum: Load of a value of an enumerated type whichis not in the range of representable values for that enumeratedtype.
-fsanitize=float-cast-overflow: Conversion to, from, orbetween floating-point types which would overflow thedestination. Because the range of representable values for allfloating-point types supported by Clang is [-inf, +inf], the onlycases detected are conversions from floating point to integer types.
-fsanitize=float-divide-by-zero: Floating point division byzero. This is undefined per the C and C++ standards, but is definedby Clang (and by ISO/IEC/IEEE 60559 / IEEE 754) as producing either aninfinity or NaN value, so is not included in-fsanitize=undefined.
-fsanitize=function: Indirect call of a function through afunction pointer of the wrong type.
-fsanitize=implicit-unsigned-integer-truncation,-fsanitize=implicit-signed-integer-truncation: Implicit conversion frominteger of larger bit width to smaller bit width, if that results in dataloss. That is, if the demoted value, after casting back to the originalwidth, is not equal to the original value before the downcast.The-fsanitize=implicit-unsigned-integer-truncationhandles conversionsbetween twounsignedtypes, while-fsanitize=implicit-signed-integer-truncationhandles the rest of theconversions - when either one, or both of the types are signed.Issues caught by these sanitizers are not undefined behavior,but are often unintentional.
-fsanitize=implicit-integer-sign-change: Implicit conversion betweeninteger types, if that changes the sign of the value. That is, if theoriginal value was negative and the new value is positive (or zero),or the original value was positive, and the new value is negative.Issues caught by this sanitizer are not undefined behavior,but are often unintentional.
-fsanitize=integer-divide-by-zero: Integer division by zero.
-fsanitize=implicit-bitfield-conversion: Implicit conversion frominteger of larger bit width to smaller bitfield, if that results in dataloss. This includes unsigned/signed truncations and sign changes, similarlyto how the-fsanitize=implicit-integer-conversiongroup works, butexplicitly for bitfields.
-fsanitize=nonnull-attribute: Passing null pointer as a functionparameter which is declared to never be null.
-fsanitize=null: Use of a null pointer or creation of a nullreference.
-fsanitize=nullability-arg: Passing null as a function parameterwhich is annotated with_Nonnull.
-fsanitize=nullability-assign: Assigning null to an lvalue whichis annotated with_Nonnull.
-fsanitize=nullability-return: Returning null from a function witha return type annotated with_Nonnull.
-fsanitize=objc-cast: Invalid implicit cast of an ObjC object pointerto an incompatible type. This is often unintentional, but is not undefinedbehavior, therefore the check is not a part of theundefinedgroup.Currently only supported on Darwin.
-fsanitize=object-size: An attempt to potentially use bytes whichthe optimizer can determine are not part of the object being accessed.This will also detect some types of undefined behavior that may notdirectly access memory, but are provably incorrect given the size ofthe objects involved, such as invalid downcasts and calling methods oninvalid pointers. These checks are made in terms of__builtin_object_size, and consequently may be able to detect moreproblems at higher optimization levels.
-fsanitize=pointer-overflow: Performing pointer arithmetic whichoverflows, or where either the old or new pointer value is a null pointer(excluding the case where both are null pointers).
-fsanitize=return: In C++, reaching the end of avalue-returning function without returning a value.
-fsanitize=returns-nonnull-attribute: Returning null pointerfrom a function which is declared to never return null.
-fsanitize=shift: Shift operators where the amount shifted isgreater or equal to the promoted bit-width of the left hand sideor less than zero, or where the left hand side is negative. For asigned left shift, also checks for signed overflow in C, and forunsigned overflow in C++. You can use-fsanitize=shift-baseor-fsanitize=shift-exponentto check only left-hand side orright-hand side of shift operation, respectively.
-fsanitize=unsigned-shift-base: check that an unsigned left-hand side ofa left shift operation doesn’t overflow. Issues caught by this sanitizer arenot undefined behavior, but are often unintentional.
-fsanitize=signed-integer-overflow: Signed integer overflow, where theresult of a signed integer computation cannot be represented in its type.This includes all the checks covered by-ftrapv, as well as checks forsigned division overflow (INT_MIN/-1). Note that checks are stilladded even when-fwrapvis enabled. This sanitizer does not check forlossy implicit conversions performed before the computation (see-fsanitize=implicit-integer-conversion). Both of these two issues are handledby-fsanitize=implicit-integer-conversiongroup of checks.
-fsanitize=unreachable: If control flow reaches an unreachableprogram point.
-fsanitize=unsigned-integer-overflow: Unsigned integer overflow, wherethe result of an unsigned integer computation cannot be represented in itstype. Unlike signed integer overflow, this is not undefined behavior, butit is often unintentional. This sanitizer does not check for lossy implicitconversions performed before such a computation(see-fsanitize=implicit-integer-conversion).
-fsanitize=vla-bound: A variable-length array whose bounddoes not evaluate to a positive value.
-fsanitize=vptr: Use of an object whose vptr indicates that it is ofthe wrong dynamic type, or that its lifetime has not begun or has ended.Incompatible with-fno-rtti. Link must be performed byclang++, notclang, to make sure C++-specific parts of the runtime library and C++standard libraries are present. The check is not a part of theundefinedgroup. Also it does not support-fsanitize-trap=vptr.
- You can also use the following check groups:
-fsanitize=undefined: All of the checks listed above other thanfloat-divide-by-zero,unsigned-integer-overflow,implicit-conversion,local-bounds,vptrand thenullability-*group of checks.-fsanitize=undefined-trap: Deprecated alias of-fsanitize=undefined.-fsanitize=implicit-integer-truncation: Catches lossy integralconversions. Enablesimplicit-signed-integer-truncationandimplicit-unsigned-integer-truncation.-fsanitize=implicit-integer-arithmetic-value-change: Catches implicitconversions that change the arithmetic value of the integer. Enablesimplicit-signed-integer-truncationandimplicit-integer-sign-change.-fsanitize=implicit-integer-conversion: Checks for suspiciousbehavior of implicit integer conversions. Enablesimplicit-unsigned-integer-truncation,implicit-signed-integer-truncation, andimplicit-integer-sign-change.-fsanitize=implicit-conversion: Checks for suspiciousbehavior of implicit conversions. Enablesimplicit-integer-conversion, andimplicit-bitfield-conversion.-fsanitize=integer: Checks for undefined or suspicious integerbehavior (e.g. unsigned integer overflow).Enablessigned-integer-overflow,unsigned-integer-overflow,shift,integer-divide-by-zero,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation, andimplicit-integer-sign-change.-fsanitize=nullability: Enablesnullability-arg,nullability-assign, andnullability-return. While violatingnullability does not have undefined behavior, it is often unintentional,so UBSan offers to catch it.
Volatile¶
Thenull,alignment,object-size,local-bounds, andvptr checks do not applyto pointers to types with thevolatile qualifier.
Minimal Runtime¶
There is a minimal UBSan runtime available suitable for use in productionenvironments. This runtime has a small attack surface. It only provides verybasic issue logging and deduplication, and does not support-fsanitize=vptrchecking.
To use the minimal runtime, add-fsanitize-minimal-runtime to the clangcommand line options. For example, if you’re used to compiling with-fsanitize=undefined, you could enable the minimal runtime with-fsanitize=undefined-fsanitize-minimal-runtime.
Stack traces and report symbolization¶
If you want UBSan to print symbolized stack trace for each error report, youwill need to:
Compile with
-g,-fno-sanitize-mergeand-fno-omit-frame-pointerto get proper debug information in your binary.Run your program with environment variable
UBSAN_OPTIONS=print_stacktrace=1.Make sure
llvm-symbolizerbinary is inPATH.
Logging¶
The default log file for diagnostics is “stderr”. To log diagnostics to anotherfile, you can setUBSAN_OPTIONS=log_path=....
Silencing Unsigned Integer Overflow¶
To silence reports from unsigned integer overflow, you can setUBSAN_OPTIONS=silence_unsigned_overflow=1. This feature, combined with-fsanitize-recover=unsigned-integer-overflow, is particularly useful forproviding fuzzing signal without blowing up logs.
Disabling instrumentation for common overflow patterns¶
There are certain overflow-dependent or overflow-prone code patterns whichproduce a lot of noise for integer overflow/truncation sanitizers. Negatedunsigned constants, post-decrements in a while loop condition and simpleoverflow checks are accepted and pervasive code patterns. However, the signalreceived from sanitizers instrumenting these code patterns may be too noisy forsome projects. To disable instrumentation for these common patterns one shoulduse-fsanitize-undefined-ignore-overflow-pattern=.
Currently, this option supports three overflow-dependent code idioms:
negated-unsigned-const
/// -fsanitize-undefined-ignore-overflow-pattern=negated-unsigned-constunsignedlongfoo=-1UL;// No longer causes a negation overflow warningunsignedlongbar=-2UL;// and so on...
unsigned-post-decr-while
/// -fsanitize-undefined-ignore-overflow-pattern=unsigned-post-decr-whileunsignedcharcount=16;while(count--){/* ... */}// No longer causes unsigned-integer-overflow sanitizer to trip
add-signed-overflow-test,add-unsigned-overflow-test
/// -fsanitize-undefined-ignore-overflow-pattern=add-(signed|unsigned)-overflow-testif(base+offset<base){/* ... */}// The pattern of `a + b < a`, and other re-orderings,// won't be instrumented (signed or unsigned types)
Pattern | Sanitizer |
|---|---|
negated-unsigned-const | unsigned-integer-overflow |
unsigned-post-decr-while | unsigned-integer-overflow |
add-unsigned-overflow-test | unsigned-integer-overflow |
add-signed-overflow-test | signed-integer-overflow |
Note:add-signed-overflow-test suppresses only the check for UndefinedBehavior. Eager Undefined Behavior optimizations are still possible. One mayremedy this with-fwrapv or-fno-strict-overflow.
You can enable all exclusions with-fsanitize-undefined-ignore-overflow-pattern=all or disable all exclusionswith-fsanitize-undefined-ignore-overflow-pattern=none. If-fsanitize-undefined-ignore-overflow-pattern is not specifiednone isimplied. Specifyingnone alongside other values also impliesnone asnone has precedence over other values – includingall.
Issue Suppression¶
UndefinedBehaviorSanitizer is not expected to produce false positives.If you see one, look again; most likely it is a true positive!
Disabling Instrumentation with__attribute__((no_sanitize("undefined")))¶
You disable UBSan checks for particular functions with__attribute__((no_sanitize("undefined"))). You can use all values of-fsanitize= flag in this attribute, e.g. if your function deliberatelycontains possible signed integer overflow, you can use__attribute__((no_sanitize("signed-integer-overflow"))).
This attribute may not besupported by other compilers, so consider using it together with#ifdefined(__clang__).
Suppressing Errors in Recompiled Code (Ignorelist)¶
UndefinedBehaviorSanitizer supportssrc andfun entity types inSanitizer special case list, that can be used to suppress error reportsin the specified source files or functions.
Runtime suppressions¶
Sometimes you can suppress UBSan error reports for specific files, functions,or libraries without recompiling the code. You need to pass a path tosuppression file in aUBSAN_OPTIONS environment variable.
UBSAN_OPTIONS=suppressions=MyUBSan.supp
You need to specify acheck you are suppressing and thebug location. For example:
signed-integer-overflow:file-with-known-overflow.cppalignment:function_doing_unaligned_accessvptr:shared_object_with_vptr_failures.so
There are several limitations:
Sometimes your binary must have enough debug info and/or symbol table, sothat the runtime could figure out source file or function name to matchagainst the suppression.
It is only possible to suppress recoverable checks. For the example above,you can additionally pass
-fsanitize-recover=signed-integer-overflow,alignment,vptr, althoughmost of UBSan checks are recoverable by default.Check groups (like
undefined) can’t be used in suppressions file, onlyfine-grained checks are supported.
Security Considerations¶
UndefinedBehaviorSanitizer’s runtime is meant for testing purposes and its usagein production environment should be carefully considered from securityperspective as it may compromise the security of the resulting executable.For security-sensitive applications consider usingMinimal Runtime or trap mode for all checks.
Supported Platforms¶
UndefinedBehaviorSanitizer is supported on the following operating systems:
Android
Linux
NetBSD
FreeBSD
OpenBSD
macOS
Windows
The runtime library is relatively portable and platform independent. If the OSyou need is not listed above, UndefinedBehaviorSanitizer may already work forit, or could be made to work with a minor porting effort.
Current Status¶
UndefinedBehaviorSanitizer is available on selected platforms starting from LLVM3.3. The test suite is integrated into the CMake build and can be run withcheck-ubsan command.
Additional Configuration¶
UndefinedBehaviorSanitizer adds static check data for each check unless it isin trap mode. This check data includes the full file name. The option-fsanitize-undefined-strip-path-components=N can be used to trim thisinformation. IfN is positive, file information emitted byUndefinedBehaviorSanitizer will drop the firstN components from the filepath. IfN is negative, the lastN components will be kept.
Example¶
For a file called/code/library/file.cpp, here is what would be emitted:
Default (No flag, or
-fsanitize-undefined-strip-path-components=0):/code/library/file.cpp-fsanitize-undefined-strip-path-components=1:code/library/file.cpp-fsanitize-undefined-strip-path-components=2:library/file.cpp-fsanitize-undefined-strip-path-components=-1:file.cpp-fsanitize-undefined-strip-path-components=-2:library/file.cpp
More Information¶
From Oracle blog, including a discussion of error messages:Improving Application Security with UndefinedBehaviorSanitizer (UBSan) and GCC
From LLVM project blog:What Every C Programmer Should Know About Undefined Behavior
From John Regehr’sEmbedded in Academia blog:A Guide to Undefined Behavior in C and C++