UndefinedBehaviorSanitizer

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.

Supporting 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, with integer_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

Supporting 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.

Disabling 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.

Enabling 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.

Enabling 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.

Disabling 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]'