blob: c8fcb65d5bc6df362a13ccbb79ba8d6c9279b34e [file] [log] [blame]
<html devsite><head>
<title>整数溢出排错功能</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>
如果发生意外的整数溢出,可能会导致内存损坏,或导致与内存访问或内存分配关联的变量中出现信息披露漏洞。为了解决这个问题,我们在 Android 7.0 中添加了 Clang 的 <a href="https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html">UndefinedBehaviorSanitizer</a> (UBSan) 有符号和无符号整数溢出排错程序,以<a href="https://android-developers.googleblog.com/2016/05/hardening-media-stack.html">强化媒体框架</a>。在 Android 9 中,我们<a href="https://android-developers.googleblog.com/2018/06/compiler-based-security-mitigations-in.html">将 UBSan 扩展为涵盖更多组件</a>,并改进了对它的编译系统支持。
</p>
<p>
如果发生有符号或无符号整数溢出,溢出排错功能可以检测可能溢出的算术指令,从而安全地中止进程。这些排错程序可以缓解根本原因是整数溢出的各种内存损坏和信息披露漏洞问题,例如原始 Stagefright 漏洞。
</p>
<h2 id="examples-and-source">示例和源代码</h2>
<p>
整数溢出排错功能 (IntSan) 由编译器提供,可在编译期间将 Instrumentation 添加到二进制文件中,以便检测算法溢出。在整个平台的各个组件(例如 <a href="https://android.googlesource.com/platform/external/libnl/+/master/Android.bp#64"><code>/platform/external/libnl/Android.bp</code></a>)中,该功能会默认处于启用状态。
</p>
<h2 id="implementation">实现</h2>
<p>
IntSan 使用了 UBSan 的有符号和无符号整数溢出排错程序。该缓解功能是在各个模块级别启用,有助于确保 Android 关键组件的安全性,因此不应停用。
</p>
<p>
强烈建议您为更多组件启用整数溢出排错功能。理想的候选组件是特权原生代码或可解析不可信用户输入的原生代码。系统会产生一小笔与排错程序关联的性能开销,具体取决于代码的使用情况和算术运算的普遍性。预计开销所占的百分比会较小,您可以测试性能是否存在问题。
</p>
<h3 id="intsan-in-makefiles">在 Makefile 中支持 IntSan</h3>
<p>
要在 Makefile 中启用 IntSan,请添加以下代码:
</p>
<pre class="prettyprint">LOCAL_SANITIZE := integer_overflow
# Optional features
LOCAL_SANITIZE_DIAG := integer_overflow
LOCAL_SANITIZE_BLACKLIST := modulename_blacklist.txt</pre>
<ul>
<li><code>LOCAL_SANITIZE</code> 会收到一个以英文逗号分隔的排错程序列表,其中 <code>integer_overflow</code> 是一组预封装的选项,用于各个有符号和无符号整数溢出排错程序,且具有<a href="https://android.googlesource.com/platform/build/soong/+/master/cc/config/integer_overflow_blacklist.txt">默认黑名单</a></li>
<li><code>LOCAL_SANITIZE_DIAG</code> 用于为排错程序启用诊断模式。请仅在测试期间使用诊断模式,因为它不会在溢出发生时中止,而这会完全抹杀缓解功能的安全优势。如需更多详细信息,请参阅<a href="#troubleshooting">问题排查</a></li>
<li><code>LOCAL_SANITIZE_BLACKLIST</code> 用于指定黑名单文件,以防止函数和源文件成为排错对象。如需更多详细信息,请参阅<a href="#troubleshooting">问题排查</a></li>
</ul>
<p>
如果您想要进行更精细的控制,请使用一个或两个标志逐个启用排错程序:
</p>
<pre class="prettyprint">LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow</pre>
<aside class="caution"><strong>注意</strong><strong>必须</strong>按照上文所述为静态二进制文件/库指定各个排错程序;<code>integer_overflow</code> 标志不支持静态二进制文件/库。在逐个指定时,请同时使用有符号和无符号排错程序。</aside>
<h3 id="intsan-in-bp">在蓝图文件中支持 IntSan</h3>
<p>
要在蓝图文件(例如 <a href="https://android.googlesource.com/platform/external/libnl/+/master/Android.bp#64"><code>/platform/external/libnl/Android.bp</code></a>)中启用整数溢出排错功能,请添加以下代码:
</p>
<pre class="prettyprint"> sanitize: {
integer_overflow: true,
diag: {
integer_overflow: true,
},
blacklist: "modulename_blacklist.txt",
},</pre>
<p>
与 Makefile 一样,<code>integer_overflow</code> 属性是一组预封装的选项,用于各个有符号和无符号整数溢出排错程序,且具有<a href="https://android.googlesource.com/platform/build/soong/+/master/cc/config/integer_overflow_blacklist.txt">默认黑名单</a>
</p>
<p>
<code>diag</code> 属性集用于为排错程序启用诊断模式。请仅在测试期间使用诊断模式。诊断模式不会在溢出发生时中止,而这会完全抹杀用户细分版本中缓解功能的安全优势。如需更多详细信息,请参阅<a href="#troubleshooting">问题排查</a>
</p>
<p>
<code>blacklist</code> 属性用于指定黑名单文件,以便开发者防止函数和源文件成为排错对象。如需更多详细信息,请参阅<a href="#troubleshooting">问题排查</a>
</p>
<p>
要逐个启用排错程序,请使用以下代码:
</p>
<pre class="prettyprint"> sanitize: {
misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
diag: {
misc_undefined: ["signed-integer-overflow",
"unsigned-integer-overflow",],
},
blacklist: "modulename_blacklist.txt",
},</pre>
<aside class="caution"><strong>注意</strong>:在 Android 9 中,<strong>必须</strong>按照上文所述为静态二进制文件/库指定各个排错程序;<code>integer_overflow</code> 标志不支持静态二进制文件/库。在逐个指定时,请同时使用有符号和无符号排错程序。</aside>
<h3 id="troubleshooting">问题排查</h3>
<p>
如果要在新组件中启用整数溢出排错功能,或者依赖于启用了整数溢出排错功能的平台库,则可能会遇到良性整数溢出导致进程中止的一些问题。您应测试启用了排错功能的组件,以确保可以发现良性溢出。
</p>
<p>
要查找由用户细分版本中的排错功能导致的进程中止,请搜索带 Abort 消息(指示 UBSan 捕获了溢出)的 <code>SIGABRT</code> 崩溃,例如:
</p>
<pre class="prettyprint">pid: ###, tid: ###, name: Binder:### &gt;&gt;&gt; /system/bin/surfaceflinger &lt;&lt;&lt;
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'ubsan: sub-overflow'</pre>
<p>
堆栈轨迹应包括导致进程中止的函数,不过,内联函数中发生的溢出在堆栈轨迹中可能不明显。
</p>
<p>
为了更轻松地确定根本原因,请在触发中止的库中启用诊断功能,尝试重现错误。<strong>启用诊断功能后,进程将不会中止</strong>,而是会继续运行。进程不中止有助于最大限度增加特定执行路径中良性溢出的数量,而无需在更正每个错误后重新进行编译。诊断功能会生成一条错误消息,其中包含导致进程中止的行号和源文件信息:
</p>
<pre class="prettyprint">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')</pre>
<p>
一旦找到存在问题的算术运算,请确保溢出是良性的且符合预期(例如没有安全隐患)。您可以通过以下方式解决排错程序中止问题:
</p>
<ul>
<li>重构代码以避免溢出(<a href="https://android-review.googlesource.com/c/platform/frameworks/av/+/572808">示例</a>
</li><li>通过 Clang 的 <a href="https://clang.llvm.org/docs/LanguageExtensions.html#checked-arithmetic-builtins">__builtin_*_overflow</a> 函数实现显式溢出(<a href="https://android-review.googlesource.com/c/platform/frameworks/av/+/588160">示例</a>
</li><li>通过属性来禁止对函数进行排错(<a href="https://android-review.googlesource.com/c/platform/frameworks/base/+/531720">示例</a>
</li><li>通过黑名单文件来禁止对函数或源文件进行排错(<a href="https://android-review.googlesource.com/c/platform/frameworks/base/+/574222">示例</a>
</li></ul>
<p>
您应尽可能使用最精细的解决方案。例如,如果某大型函数具有多个算术运算和单个溢出运算,则应对单个溢出运算进行重构,而不是将整个函数列入黑名单。
</p>
<p>
可能导致良性溢出的常见模式包括:
</p><ul>
<li>在转换为有符号类型之前发生无符号溢出的隐式类型转换(<a href="https://android-review.googlesource.com/c/platform/frameworks/av/+/574011">示例</a>
</li><li>删除关联的列表,且删除时循环索引会递减(<a href="https://android-review.googlesource.com/c/platform/frameworks/base/+/588158">示例</a>
</li><li>将无符号类型指定为 -1,作为最大值的简写形式(<a href="https://android-review.googlesource.com/c/platform/frameworks/native/+/574088/1/services/surfaceflinger/Layer.cpp">示例</a>
</li><li>在条件中使无符号整数递减的循环(<a href="https://android-review.googlesource.com/c/platform/frameworks/native/+/573763/1/services/inputflinger/InputReader.cpp">示例</a><a href="https://android-review.googlesource.com/c/platform/frameworks/rs/+/572756">示例</a></li></ul>
<p>
在停用排错功能之前,开发者最好是确保排错程序检测到的溢出确实是良性的,并且没有意外的副作用或安全隐患。
</p>
<h3 id="disabling-intsan">停用 IntSan</h3>
<p>
您可以使用黑名单或函数属性来停用 IntSan。请仅在重构代码不合理或者存在有问题的性能开销时,谨慎停用 IntSan。
</p>
<p>
要详细了解如何使用<a href="https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#disabling-instrumentation-with-attribute-no-sanitize-undefined">函数属性</a><a href="https://clang.llvm.org/docs/SanitizerSpecialCaseList.html">黑名单文件格式设置</a>停用 IntSan,请参阅上游 Clang 文档。应将黑名单的作用范围限定为特定的排错程序,方法是使用区块名称指定目标排错程序,以免影响其他排错程序。
</p>
<h2 id="validation">验证</h2>
<p>目前没有专门针对整数溢出排错功能的 CTS 测试。因此请确保 CTS 测试在启用或未启用 IntSan 的情况下均能通过,以证明它不会给设备带来影响。
</p>
</body></html>