UndefinedBehaviorSanitizer (UBSan) performs compile-time instrumentation to check for various types of undefined behavior. While UBSan is capable of detecting many undefined behavior bugs, Android supports:
- alignment
- bool
- bounds
- enum
- float-cast-overflow
- float-divide-by-zero
- integer-divide-by-zero
- nonnull-attribute
- null
- return
- returns-nonnull-attribute
- shift-base
- shift-exponent
- signed-integer-overflow
- unreachable
- unsigned-integer-overflow
- vla-bound
unsigned-integer-overflow, while not technically undefined behavior, is included in the sanitizer and used in many Android modules, including the mediaserver components, to eliminate any latent integer-overflow vulnerabilities.
Implementation
In the Android build system, you can enable UBSan globally or locally. To enable UBSan globally, set SANITIZE_TARGET in Android.mk. To enable UBSan at a per-module level, set LOCAL_SANITIZE and specify the undefined behaviors that you want to look for in Android.mk. For example:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0 LOCAL_SRC_FILES:= sanitizer-status.c LOCAL_MODULE:= sanitizer-status LOCAL_SANITIZE := alignment bounds null unreachable integer LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer include $(BUILD_EXECUTABLE)
And the equivalent blueprint (Android.bp) configuration:
cc_binary { cflags: [ "-std=c11", "-Wall", "-Werror", "-O0", ], srcs: ["sanitizer-status.c"], name: "sanitizer-status", sanitize: { misc_undefined: [ "alignment", "bounds", "null", "unreachable", "integer", ], diag: { misc_undefined: [ "alignment", "bounds", "null", "unreachable", "integer", ], }, }, }
UBSan shortcuts
Android also has two shortcuts, integer
and
default-ub
, to enable a set of sanitizers at the same time. integer
enables integer-divide-by-zero
,
signed-integer-overflow
and unsigned-integer-overflow
.
default-ub
enables the checks that have minimal compiler
performance issues: bool, integer-divide-by-zero, return,
returns-nonnull-attribute, shift-exponent, unreachable and vla-bound
. The
integer sanitizer class can be used with SANITIZE_TARGET and LOCAL_SANITIZE,
while default-ub can only be used with SANITIZE_TARGET.
Better error reporting
Android's default UBSan implementation invokes a specified function when undefined behavior is encountered. By default, this function is abort. However, starting in October 2016, UBSan on Android has an optional runtime library that gives more detailed error reporting, including type of undefined behavior encountered, file and source code line information. To enable this error reporting with integer checks add the following to an Android.mk file:
LOCAL_SANITIZE:=integer LOCAL_SANITIZE_DIAG:=integer
The LOCAL_SANITIZE value enables the sanitizer during the build. LOCAL_SANITIZE_DIAG turns on diagnostic mode for the specified sanitizer. It is possible to set LOCAL_SANITIZE and LOCAL_SANITIZE_DIAG to different values, but only those checks in LOCAL_SANITIZE are enabled. If a check is not specified in LOCAL_SANITIZE, but is specified in LOCAL_SANITIZE_DIAG, the check isn't enabled and diagnostic messages aren't given.
Here is an example of the information provided by the UBSan runtime library:
pixel-xl:/ # sanitizer-status ubsan sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')
Integer overflow sanitization
Unintended integer overflows can cause memory corruption or information disclosure vulnerabilities in variables associated with memory accesses or memory allocations. To combat this, we added Clang's UndefinedBehaviorSanitizer (UBSan) signed and unsigned integer overflow sanitizers to harden the media framework in Android 7.0. In Android 9, we expanded UBSan to cover more components and improved build system support for it.
This is designed to add checks around arithmetic operations / instructions—which might overflow—to safely abort a process if an overflow does happen. These sanitizers can mitigate an entire class of memory corruption and information disclosure vulnerabilities where the root cause is an integer overflow, such as the original Stagefright vulnerability.
Examples and source
Integer Overflow Sanitization (IntSan) is provided by the compiler and adds
instrumentation into the binary during compile time to detect arithmetic
overflows. It is enabled by default in various components throughout the
platform, for example
/platform/external/libnl/Android.bp
.
Implementation
IntSan uses UBSan's signed and unsigned integer overflow sanitizers. This mitigation is enabled on a per-module level. It helps keep critical components of Android secure and should not be disabled.
We strongly encourage you to enable Integer Overflow Sanitization for additional components. Ideal candidates are privileged native code or native code that parses untrusted user input. There is a small performance overhead associated with the sanitizer that is dependent on code's usage and the prevalence of arithmetic operations. Expect a small overhead percentage and test if performance is a concern.
Support IntSan in makefiles
To enable IntSan in a makefile, add:
LOCAL_SANITIZE := integer_overflow # Optional features LOCAL_SANITIZE_DIAG := integer_overflow LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE
takes a comma separated list of sanitizers, withinteger_overflow
being a pre-packaged set of options for the individual signed and unsigned integer overflow sanitizers with a default BLOCKLIST.LOCAL_SANITIZE_DIAG
turns on diagnostics mode for the sanitizers. Use diagnostics mode only during testing because this will not abort on overflows, completely negating the security advantage of the mitigation. See Troubleshooting for additional details.LOCAL_SANITIZE_BLOCKLIST
allows you to specify a BLOCKLIST file to prevent functions and source files from being sanitized. See Troubleshooting for additional details.
If you want more granular control, enable the sanitizers individually using one or both flags:
LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow
Support IntSan in blueprint files
To enable integer overflow sanitization in a blueprint file, such as
/platform/external/libnl/Android.bp
,
add:
sanitize: { integer_overflow: true, diag: { integer_overflow: true, }, BLOCKLIST: "modulename_BLOCKLIST.txt", },
As with make files, the integer_overflow
property is a pre-packaged
set of options for the individual signed and unsigned integer overflow
sanitizers with a default
BLOCKLIST.
The diag
set of properties enables diagnostics mode for the
sanitizers. Use diagnostics mode only during testing. Diagnostics mode doesn't
abort on overflows, which completely negates the security advantage of the
mitigation in user builds. See Troubleshooting for additional details.
The BLOCKLIST
property allows specification of a BLOCKLIST file
that allows developers to prevent functions and source files from being
sanitized. See Troubleshooting for
additional details.
To enable the sanitizers individually, use:
sanitize: { misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"], diag: { misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow",], }, BLOCKLIST: "modulename_BLOCKLIST.txt", },
Troubleshooting
If you are enabling integer overflow sanitization in new components, or rely on platform libraries that have had integer overflow sanitization, you may run into a few issues with benign integer overflows causing aborts. You should test components with sanitization enabled to ensure benign overflows can be surfaced.
To find, aborts caused by sanitization in user builds, search for
SIGABRT
crashes with Abort messages indicating an overflow caught
by UBSan, such as:
pid: ###, tid: ###, name: Binder:### >>> /system/bin/surfaceflinger <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: sub-overflow'
The stack trace should include the function causing the abort, however, overflows that occur in inline functions may not be evident in the stack trace.
To more easily determine the root cause, enable diagnostics in the library triggering the abort and attempt to reproduce the error. With diagnostics enabled, the process will not abort and will instead continue to run. Not aborting helps maximize the number of benign overflows in a particular execution path without having to recompile after fixing each bug. Diagnostics produces an error message which includes the line number and source file causing the abort:
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')
Once the problematic arithmetic operation is located, ensure that the overflow is benign and intended (e.g. has no security implications). You can address the sanitizer abort by:
- Refactoring the code to avoid the overflow (example)
- Overflow explicitly via Clang's __builtin_*_overflow functions (example)
- Disabling sanitization in the function by specifying the
no_sanitize
attribute (example) - Disabling sanitization of a function or source file via a BLOCKLIST file (example)
You should use the most granular solution possible. For example, a large function with many arithmetic operations and a single overflowing operation should have the single operation refactored rather than the entire function BLOCKLISTed.
Common patterns that may result in benign overflows include:
- Implicit casts where an unsigned overflow occurs before being cast to a signed type (example)
- Linked list deletions which decrements the loop index on deletion (example)
- Assigning an unsigned type to -1 instead of specifying the actual max value (example)
- Loops which decrement an unsigned integer in the condition (example, example)
It is recommended that developers assure that cases where the sanitizer detects an overflow that it is indeed benign with no unintended side-effects or security implications before disabling sanitization.
Disable IntSan
You can disable IntSan with BLOCKLISTs or function attributes. Disable sparingly and only when refactoring the code is otherwise unreasonable or if there is problematic performance overhead.
See the upstream Clang documentation for more information on disabling IntSan with function attributes and BLOCKLIST file formatting. BLOCKLISTing should be scoped to the particular sanitizer by using section names specifying the target sanitizer to avoid impacting other sanitizers.
Validation
Currently, there are no CTS test specifically for Integer Overflow Sanitization. Instead, make sure that CTS tests pass with or without IntSan enabled to verify that it is not impacting the device.
Bounds sanitization
BoundsSanitizer (BoundSan) adds instrumentation to binaries to insert bounds checks around array accesses. These checks are added if the compiler cannot prove at compile time that the access will be safe and if the size of the array will be known at runtime, so that it can be checked against. Android 10 deploys BoundSan in Bluetooth and codecs. BoundSan is provided by the compiler and is enabled by default in various components throughout the platform.
Implementation
BoundSan uses UBSan's bounds sanitizer. This mitigation is enabled on a per-module level. It helps keep critical components of Android secure and shouldn't be disabled.
We strongly encourage you to enable BoundSan for additional components. Ideal candidates are privileged native code or complex native code that parses untrusted user input. The performance overhead associated with enabling BoundSan is dependent on the number of array accesses that can't be proven safe. Expect a small overhead percentage on average and test if performance is a concern.
Enable BoundSan in blueprint files
BoundSan can be enabled in blueprint files by adding "bounds"
to the misc_undefined
sanitize property for binary and library
modules:
sanitize: { misc_undefined: ["bounds"], diag: { misc_undefined: ["bounds"], }, BLOCKLIST: "modulename_BLOCKLIST.txt",
diag
The diag
property enables diagnostics mode for the sanitizers.
Use diagnostics mode only during testing. Diagnostics mode doesn't abort on
overflows, which negates the security advantage of the mitigation and carries a
higher performance overhead, so it's not recommended for production builds.
BLOCKLIST
The BLOCKLIST
property allows the specification of a BLOCKLIST
file that developers can use to prevent functions and source files from being
sanitized. Use this property only if performance is a concern and the targeted
files/functions contribute substantially. Manually audit these files/functions
to ensure that array accesses are safe. See Troubleshooting for additional
details.
Enable BoundSan in makefiles
BoundSan can be enabled in makefiles by adding "bounds"
to the LOCAL_SANITIZE
variable for binary and library modules:
LOCAL_SANITIZE := bounds # Optional features LOCAL_SANITIZE_DIAG := bounds LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE
accepts a list of sanitizers separated by a
comma.
LOCAL_SANITIZE_DIAG
turns on diagnostics mode. Use diagnostics
mode only during testing. Diagnostics mode doesn't abort on overflows, which
negates the security advantage of the mitigation and carries a higher
performance overhead, so it's not recommended for production builds.
LOCAL_SANITIZE_BLOCKLIST
allows specification of a BLOCKLIST
file that allows developers to prevent functions and source files from being
sanitized. Use this property only if performance is a concern and the targeted
files/functions contribute substantially. Manually audit these files/functions
to ensure that array accesses are safe. See Troubleshooting for additional
details.
Disable BoundSan
You can disable BoundSan in functions and source files with BLOCKLISTs or function attributes. It's best to keep BoundSan enabled, so only disable it if the function or file is creating a large amount of performance overhead and the source has been manually reviewed.
For more Information on disabling BoundSan with function attributes and BLOCKLIST file formatting, refer to the Clang LLVM documentation. Scope the BLOCKLISTing to the particular sanitizer by using section names specifying the target sanitizer to avoid impacting other sanitizers.
Validation
There are no CTS test specifically for BoundSan. Instead, make sure that CTS tests pass with or without BoundSan enabled to verify that it isn't impacting the device.
Troubleshooting
Thoroughly test components after enabling BoundSan to ensure that any previously undetected out-of-bounds accesses are addressed.
BoundSan errors can be easily identified as they include the following tombstone abort message:
pid: ###, tid: ###, name: Binder:### >>> /system/bin/foobar <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: out-of-bounds'
When running in diagnostics mode, the source file, line number, and index
value are printed to logcat
. By default, this mode doesn't
throw an abort message. Review logcat
to check for any
errors.
external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'