blob: 7fd4825e769002fe9a39759613406a464e7f0158 [file] [log] [blame]
<html devsite>
<head>
<title>Control Flow Integrity</title>
<meta name="project_path" value="/_project.yaml" />
<meta name="book_path" value="/_book.yaml" />
</head>
<body>
<!--
Copyright 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
//www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>
As of 2016, about 86% of all vulnerabilities on Android are memory safety
related. Most vulnerabilities are exploited by attackers changing the normal
control flow of an application to perform arbitrary malicious activities with
all the privileges of the exploited application.
<a href="https://clang.llvm.org/docs/ControlFlowIntegrity.html">Control flow
integrity</a> (CFI) is a security mechanism that disallows changes to the
original control flow graph of a compiled binary, making it significantly harder
to perform such attacks.
</p>
<p>
In Android 8.1, we enabled LLVM's implementation of CFI in the media stack. In
Android 9, we enabled CFI in more components and also the kernel. System CFI is
on by default but you need to enable kernel CFI.
</p>
<p>
LLVM's CFI requires compiling with
<a href="https://llvm.org/docs/LinkTimeOptimization.html">Link-Time Optimization
(LTO)</a>. LTO preserves the LLVM bitcode representation of object files until
link-time, which allows the compiler to better reason about what optimizations
can be performed. Enabling LTO reduces the size of the final binary and improves
performance, but increases compile time. In testing on Android, the combination
of LTO and CFI results in negligible overhead to code size and performance; in a
few cases both improved.
</p>
<p>
For more technical details about CFI and how other forward-control checks are
handled, see the <a
href="https://clang.llvm.org/docs/ControlFlowIntegrityDesign.html">LLVM design
documentation</a>.
</p>
<h2 id="examples">Examples and source</h2>
<p>
CFI is provided by the compiler and adds instrumentation into the binary during
compile time. We support CFI in the Clang toolchain and the Android build system
in AOSP.
</p>
<p>
CFI is enabled by default for Arm64 devices for the set of components in
<code><a
href="https://android.googlesource.com/platform/build/+/master/target/product/cfi-common.mk">/platform/build/target/product/cfi-common.mk</a></code>.
It's also directly enabled in a set of media components' makefiles/blueprint
files, such as <code><a
href="https://android.googlesource.com/platform/frameworks/av/+/master/media/libmedia/Android.bp#117">/platform/frameworks/av/media/libmedia/Android.bp</a></code>
and <code><a
href="https://android.googlesource.com/platform/frameworks/av/+/master/cmds/stagefright/Android.mk#188">/platform/frameworks/av/cmds/stagefright/Android.mk</a></code>.
<h2 id="system-cfi">Implementing system CFI</h2>
<p>
CFI is enabled by default if you use Clang and the Android build system.
Because CFI helps keep Android users safe, you should not disable it.
</p>
<p>
In fact, we strongly encourage you to enable CFI for additional components.
Ideal candidates are privileged native code, or native code that processes
untrusted user input. If you're using clang and the Android build system, you
can enable CFI in new components by adding a few lines to your makefiles or
blueprint files.
</p>
<h3 id="cf-in-mk">Supporting CFI in makefiles</h3>
<p>
To enable CFI in a make file, such as <code><a
href="https://android.googlesource.com/platform/frameworks/av/+/master/cmds/stagefright/Android.mk#188">/platform/frameworks/av/cmds/stagefright/Android.mk</a></code>,
add:
<pre
class="prettyprint">LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt</pre>
<ul>
<li><code>LOCAL_SANITIZE</code> specifies CFI as the sanitizer during the
build.</li>
<li><code>LOCAL_SANITIZE_DIAG</code> turns on diagnostic mode for CFI.
Diagnostic mode prints out additional debug information in logcat during
crashes, which is useful while developing and testing your builds. Make
sure to remove diagnostic mode on productions builds, though.</li>
<li><code>LOCAL_SANITIZE_BLACKLIST</code> allows components to selectively
disable CFI instrumentation for individual functions or source files. You
can use a blacklist as a last resort to fix any user-facing issues that
might otherwise exist. For more details, see
<a href="#disabling-cfi">Disabling CFI</a>.</li>
</ul>
<h3 id="cfi-in-bp">Supporting CFI in blueprint files</h3>
<p>
To enable CFI in a blueprint file, such as <code><a
href="https://android.googlesource.com/platform/frameworks/av/+/master/media/libmedia/Android.bp#117">/platform/frameworks/av/media/libmedia/Android.bp</a></code>,
add:</p>
<pre class="prettyprint"> sanitize: {
cfi: true,
diag: {
cfi: true,
},
blacklist: "cfi_blacklist.txt",
},</pre>
<h3 id="troubleshooting">Troubleshooting</h3>
<p>
If you're enabling CFI in new components, you may run into a few issues with
<em>function type mismatch errors</em> and <em>assembly code type mismatch
errors</em>.
</p>
<p>
Function type mismatch errors occur because CFI restricts indirect calls to only
jump to functions that have the same dynamic type as the static type used in the
call. CFI restricts virtual and non-virtual member function calls to only jump
to objects that are a derived class of the static type of the object used to
make the call. This means, when you have code that violates either of these
assumptions, the instrumentation that CFI adds will abort. For example, the
stack trace shows a SIGABRT and logcat contains a line about control flow
integrity finding a mismatch.
</p>
<p>
To fix this, ensure that the called function has the same type that was
statically declared. Here are two example CLs:
</p>
<ul>
<li><strong>Bluetooth</strong>:
<a href="https://android-review.googlesource.com/c/platform/system/bt/+/532377">/c/platform/system/bt/+/532377</a></li>
<li><strong>NFC</strong>:
<a href="https://android-review.googlesource.com/c/platform/system/nfc/+/527858">/c/platform/system/nfc/+/527858</a></li>
</ul>
<p>
Another possible issue is trying to enable CFI in code that contains indirect
calls to assembly. Because assembly code is not typed, this results in a type
mismatch.
</p>
<p>
To fix this, create native code wrappers for each assembly call, and give the
wrappers the same function signature as the calling poiner. The wrapper can then
directly call the assembly code. Because direct branches are not instrumented by
CFI (they cannot be repointed at runtime and so do not pose a security risk),
this will fix the issue.
</p>
<p>
If there are too many assembly functions and they cannot all be fixed, you can
also blacklist all functions that contain indirect calls to assembly. This is
not recommended as it disables CFI checks on these functions, thereby opening
attack surface.
</p>
<h3 id="disabling-cfi">Disabling CFI</h3>
<p>
We didn't observe any performance overhead, so you shouldn't need to disable
CFI. However, if there is a user-facing impact, you can selectively disable CFI
for individual functions or source files by supplying a sanitizer blacklist file
at compile time. The blacklist instructs the compiler to disable CFI
instrumentation in specified locations.
</p>
<p>
The Android build system provides support for per-component blacklists (allowing
you to choose source files or individual functions that will not receive CFI
instrumentation) for both Make and Soong. For more details on the format of a
blacklist file, see the <a
href="https://clang.llvm.org/docs/ControlFlowIntegrity.html#blacklist">upstream
Clang docs</a>.
</p>
<h2 id="validation">Validation</h2>
<p>
Currently, there are no CTS test specifically for CFI. Instead, make sure that
CTS tests pass with or without CFI enabled to verify that CFI isn't impacting
the device.
</p>
</body>
</html>