Google is committed to advancing racial equity for Black communities. See how.

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_BLACKLIST := modulename_blacklist.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 blacklist.
  • 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_BLACKLIST allows you to specify a blacklist 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,
      },
      blacklist: "modulename_blacklist.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 blacklist.

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 blacklist property allows specification of a blacklist 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",],
      },
      blacklist: "modulename_blacklist.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 blacklist 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 blacklisted.

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 blacklists 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 blacklist file formatting. Blacklisting 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.