Fortify SCA详细
1.1 Fortify SCA概述
1、Source Code Analysis 阶段概述
Audit Workbench 会启动 Fortify SCA“Scanning(扫描)”向导来扫描和分析源代码。该向导整合了以下几个分析阶段:
转换:
使用源代码创建中间文件,源代码与一个 Build ID相关联,Build ID通常就是项目名称。扫描与分析:
扫描中间文件,分析代码,并将结果写入一个Fortify Project Results(FPR)文件。校验:
确保所有源文件均包含在扫描过程中,使用的是正确的规则包,且没有报告重大错误。
2、安全编码规则包概述
安全编码规则包是 Fortify Software 安全研究小组多年软件安全经验的体现,并且经过其不断努力改进而成。这些规则是通过对编码理论和常用编码实践的研究,而取得的软件安全知识的巨大积累,并且在 Fortify Software 安全研究小组的努力下不断扩展和改进。每个安全编码规则包均包含大量的规则,每个规则定义了一个被 source code analysis 检测出的特定异常行为。
一旦检测出安全问题,安全编码规则包会提供有关问题的信息,让开发人员能够计划并实施修复工作,这样比研究问题的安全细节更为有效。这些信息包括关于问题类别的具体信息、该问题会如何被攻击者利用,以及开发人员如何确保代码不受此漏洞的威胁。
安全编码规则包支持多种编程语言,也支持各种经过扩展的第三方库和配置文件。
有关当前安全编码规则包的信息,请参见《安全编码规则包参考》。
3、开始页面概述
开始页面包含以下几个区域:
Start New Project(启动新项目):
启动源代码扫描向导。Custom Rules Editor(自定义规则编辑器):
启动用于创建和检查安全规则或编码实践规则的工具,这些规则可针对您的源代码进行自定义。Open Project(打开项目):
单击某一项目名称可打开最近的项目;或使用“Open Project(打开项目)”链接可定位到某一项目。
fortify\bin\AuditWorkbench.cmd 开始页面:
4、审计界面概述
下图显示了“Auditing(审计)”界面中的 Webgoat FPR 文件示例。
Audit Workbench 界面:
Audit Workbench 界面由以下几个面板组成:
“Issues(问题)”面板
“Analysis Trace(分析跟踪)”面板
“Project Summary(项目摘要)”面板
“Source Code Viewer(源代码查看器)”面板
“Function(函数)”面板
“Issue Auditing(问题审计)”面板
“Issues(问题)”面板
使用 Issues(问题)面板,您可以对希望审计的问题进行分组和选择。该面板由下列元素组成:
“Filter Set(过滤器组)”下拉列表
“Folders(文件夹)”选项卡
Group by(分组方式)
“Search(搜索)”框
问题面板:
“Filter Set(过滤器组)”下拉列表
过滤器组控制着“Issue(问题)”面板的设置和显示属性。过滤器组是项目配置的一部分。您可以为每个项目自定义不同的过滤器组。每个项目均可具有唯一的过滤器组。
“Filter Set(过滤器组)”部分包含以下设置:
文件夹的名称和颜色。要进行配置,请选择 Tools(工具)- Project Configuration(项目配置)- Folder Settings(文件夹设置)。
问题列出的位置,以及是否列出。要进行配置,请选择 Options(选项)- Show View(显示视图)- Filters(过滤器)。
注意:“Audit Guide Visibility Filters(审计指南可见性过滤器)”配置适用于整个项目。
Fortify Software 提供了默认过滤器组:Broad(广泛)、Medium(普通)、Targeted(特定)和 Developer(开发人员)。“Filter Sets(过滤器组)”将问题按严重性归类于“Hot(严重)”、“Warning(警告)”和“Info(信息)”文件夹中。 所有过滤器组都具有相同的文件夹过滤器。
不同“Filter Sets(过滤器组)”中会显示和隐藏不同的问题:
Broad(广泛):
使用最全面的规则组显示问题。应使用该过滤器组来发现要审计的一组广泛的安全问题。Medium(普通):
显示了在产生结果(详述潜在问题)和产生预期可能的漏洞组之间打破平衡的问题。Targeted(特定):
显示在多种行业内及各种环境下经证实的具有高优先级的问题类别。应使用这些过滤器组来发现一组有限的众所周知的关键安全问题。Developer(开发人员):
显示开发人员尤为关注的问题,如高准确性 bug。
“Folders(文件夹)”选项卡:
“Issues(问题)”面板上的选项卡称为“文件夹”。使用“Project Configuration(项目配置)”,您可以自定义文件夹及其设置。因此,不同过滤器组和项目之间的文件夹数目、名称、颜色和问题列表各不相同。
文件夹过滤器将问题归类于各种文件夹中。如果某个问题与任何文件夹过滤器都不匹配,该问题将被列在默认文件夹中。可见性过滤器决定了某个问题是否显示在列表中。
注意:选择 Option(选项)- Show Suppressed(显示已废除项)、Show Hidden(显示隐藏项)(显示与可见性过滤器匹配的项目),以及 Show Removed(显示删除项)可显示所有项目。
Group by(分组方式):
“Group by(分组方式)”选项用于将问题列表归类到各个子文件夹中。选定的选项会应用于所有可见文件夹。使用 <none(无)> 选项,可列出文件夹中的所有项目而不进行任何分组。
使用 Audit Workbench,通过更改对分组进行归类所依据的属性、添加或删除属性以创建子分组,以及添加您自己的分组选项,您可以对现有分组进行自定义。
“Group by(分组方式)”设置适用于 Audit Workbench 实例。您可以将分组方式选项应用于任何利用 Audit Workbench 实例打开的项目。
“Search(搜索)”框:
使用 Search(搜索)字段,您可以限制文件夹中显示的问题和搜索特定的问题。有关搜索功能的信息,请参见使用搜索。
“Analysis Trace(分析跟踪)”面板:
当您选择某个问题后,Analysis Trace(分析跟踪)面板会显示相关的 trace output。通常情况下,这是一系列进程点,显示了分析器是如何找到该问题的。对于数据流和控制流问题,这一系列点会以执行顺序显示。
例如,如果您选择某个与可能被感染数据流相关的问题,Analysis Trace(分析跟踪)面板会显示这段源代码中数据流的移动方向。
分析跟踪面板
“Analysis Trace(分析跟踪)”面板使用以下图标来显示本段源代码中数据流的移动方式:
表 1:分析跟踪图标
“Project Summary(项目摘要)”面板:
“Project Summary(项目摘要)”面板提供了关于扫描的详细信息。
“Summary(摘要)”选项卡
“Certification(认证)”选项卡
“Build Information(Build 信息)”选项卡
“Analysis Information(分析信息)”选项卡
项目摘要面板
“Summary(摘要)”选项卡:
“Summary(摘要)”选项卡显示了关于本项目的高级信息。
“Certification(认证)”选项卡:
“Certification(认证)”选项卡显示了结果认证状态。结果认证用于检查 FPR 文件是否与 Fortify SCA 所生成的文件一致。
“Build Information(Build 信息)”选项卡:
“Build Information(Build 信息)”选项卡显示了以下 Fortify SCA 扫描信息:
Build 详细信息,如 Build ID、扫描的文件数、扫描日期,可能会与文件的转换日期不相同
扫描的文件列表,包括文件大小和时间戳
Java 类路径
“Analysis Information(分析信息)”选项卡:
“Analysis Information(分析信息)”选项卡显示了 Fortify SCA 的版本、计算机详细信息以及执行扫描的用户。
“Analysis Information(分析信息)”子选项卡包含以下信息:
规则包:列出了用于扫描源代码的规则包,包括每个规则包的名称、ID 和版本号。
属性:显示 Fortify SCA 属性文件的设置
命令行参数:显示用来分析项目的命令行选项
警告:列出了分析期间发生的所有错误和警告
“Source Code Viewer(源代码查看器)”面板:
“Source Code Viewer(源代码查看器)”面板显示了与在“Issues(问题)”面板中选择的问题相关联的代码段。如果“Analysis Trace(分析跟踪)”面板中的多个节点代表一个问题,则“Source Code Viewer(源代码查看器)”面板会显示与所选节点相关联的代码。
在“Source Code Viewer(源代码查看器)”面板中,您可以使用代码辅助功能创建自定义规则和新问题,如下所示:
要为某个函数创建规则,请将光标放在该函数中,单击鼠标右键,然后选择 Write Rule for This Function(为此函数编写规则)。
要创建新问题,请将光标放在该函数中,单击鼠标右键,然后选择 Create New Issue(创建新问题)。
“Function(函数)”面板:
Function(函数)面板显示了项目中的函数/方法列表。使用“Function(函数)”面板可找出该函数在源代码中的位置,了解函数是否应用和匹配某个规则,以及编写和验证自定义规则。
“Function(函数)”面板具有以下选项:
Show(显示):
确定显示在该列表中的函数。Group by(分组方式):
将函数归类到各个包和类中,显示层次结构或直接显示所有函数而不进行分组。Include unused functions(包括未使用的函数):
显示源代码中的每个函数。默认情况下,列表中不会包含未使用的函数。Legend(图例):
显示一个用于解释各个列表图标的窗口。函数旁边的图标颜色指明该函数是否应用了规则,如下所示:
--------红色:未应用任何规则
--------蓝色:可以应用规则,但没有匹配的规则
--------绿色:可以应用规则,并且有匹配的规则Search(搜索):
仅限于显示那些包含您输入的字符串的函数。该字段不区分大小写,如果该字段为空,将显示所选组中的所有函数。
函数面板
右键单击某个函数可显示以下选项:
Open Declaration(打开声明):
显示该函数在源代码中声明的位置。Find Usages(查找用法):
创建函数所应用于的文件位置的列表。会在“Audit(审计)”面板中会打开“Search(搜索)”选项卡,其中显示它所在的文件名称。Generate Rule for Function(为此函数生成规则):
启动“Custom Rules(自定义规则)”向导来编写将应用于该函数的规则。Show Matched Rule(显示匹配的规则):
列出了与该函数相匹配的所有规则所属规则包的规则 ID 和文件名。
“Issue Auditing(问题审计)”面板:
“Issue Auditing(问题审计)”面板在以下一组选项卡中提供了有关各问题的详细信息:
Summary(摘要)
Details(详细信息)
Recommendations(建议)
History(历史记录)
Diagram(图示)
Filter(过滤器)
注意:使用 Options(选项)- Show View(显示视图)菜单可显示或隐藏“Issue Auditing(问题审计)”面板中的选项卡。
Summary(摘要):
Summary(摘要)选项卡显示了关于当前所选问题的以下信息:
问题审计面板中的摘要选项卡
下表描述了“Summary(摘要)”面板中的各个选项:
元素 | 描述 |
问题 | 显示问题的位置,包括文件名和行号 |
Custom Tags | |
(自定义标签) | 区域 显示审计员可以作为问题属性添加的值的下拉列表。例如,默认情况下,“Analysis(分析)”标签提供了以下值: - Not an issue(不是问题) - Reliability issue(可靠性问题) - Unknown(未知) - Suspicious(可疑) 注意:自定义标签即项目设置,可选择“Tools(工具)- Project Configuration(项目配置)”来更改标签值。 |
Suppress Issue(废除问题) | 更改严重级别以废除和删除导航树中的问题。 |
File Bug(报告 bug) | 提供访问 bug 跟踪系统(如 Bugzilla)的途径。 |
Comments(注释) | 向注释字段中追加有关问题的其他信息。 |
规则信息 | 显示用于描述问题的信息,如所属类别和领域。 |
更多信息 | 打开 Details(详细信息)选项卡。 |
Recommendations(建议) | 打开 Recommendations(建议)选项卡。 |
Details(详细信息):
Details(详细信息)选项卡提供了有关所选问题的详细说明,并提供了用于解决该问题的指导方针。每项说明均包括下表中所述的部分或全部栏目。
元素 | 描述 |
Abstract(概要) | 对问题的概要描述 |
Explanation(解释) | 关于会在哪些情况下发生此类问题的描述,同时还包括该漏洞的讨论、通常与该漏洞相关联的代码结构、该漏洞的利用方式,以及潜在的衍生攻击类型 |
实例 ID | 问题的唯一识别符 |
规则 ID | 标识用于发现问题的主要规则 |
SCA Confidence(SCA 可信度) | 一个由 Fortify SCA 生成的衡量指标 |
+ | 最大化选项卡 |
Recommendations(建议)
“Recommendations(建议)”选项卡包含有关如何避免引发该漏洞或修改该漏洞方法的建议与示例。
元素 | 描述 |
Recommendations(建议) | 关于如何解决问题的信息 |
Tips(提示) | 提供普通或者难以审计的情况的示例,给出了有关如何识别特定问题的提示 |
References(参考信息) | 参考相关信息 |
+ | 最大化选项卡 |
History(历史记录)
显示完整的审计操作列表,其中包括以下详细信息:日期和时间、执行审计的计算机以及修正该问题的用户名称。
Diagram(图示)
“Diagram(图示)”选项卡形象地说明了在“Issues(问题)”面板上所选问题的节点执行顺序、调用深度以及表达式类型。
SQL Injection 问题数据流图示示例
“Diagram(图示)”选项卡显示了与规则类型相关的信息,如下所示:
数据流规则:
纵轴显示执行顺序。跟踪会从顶部第一个调用感染源的函数开始,然后追踪对该 source(蓝色节点)的调用情况,并在 sink(红色节点)处结束跟踪。在图示中,也会标记 source (src) 和 sink 结点。纵轴上的红 X 表示这个被调用的函数已结束执行。
横轴显示调用深度。用一条线显示控制权的传递方向。如果控制权传递时携带有被感染数据的变量,该线条是为红色;如果没有被感染数据,则该线条为黑色。
对于其他所有规则:
纵轴显示执行顺序。
每个节点的表达式类型用以下图标之一表示:
Filter(过滤器)
“Filter(过滤器)”选项卡显示了所选过滤器组中的所有过滤器。
过滤器选项卡中的选项
元素 | 描述 |
Filter | 显示所选过滤器组中所配置的可见性过滤器和文件夹过滤器的列表。 右键单击某个过滤器可显示与该过滤器匹配的问题,也可启用或禁用该过滤器。 |
If | 显示过滤器条件。 第一个下拉列表显示了问题属性的列表,第二个下拉列表指定了属性的匹配方式,而第三个列表是过滤器所匹配的值。 |
Then | 指示过滤器的类型,其中,“hide” 表示可见性过滤器,“folder” 表示文件夹过滤器。 |
1.2 使用Fortify SCA
1、扫描 Java 项目
“Scan Java Project(扫描 Java 项目)”向导将转换阶段和分析阶段合并为一个简单的步骤。使用此功能可以扫描源那些代码位于单个目录中的小型 Java 项目。
使用步骤 | 详细描述 |
扫描新的 Java 项目 | 打开 Audit Workbench。 |
系统会显示开始页面 | 在“New Projects(新项目)”中,单击 Scan Java Project(扫描 Java 项目)。 |
系统会显示 Browse for Folder(浏览文件夹)对话框 | 选择包含所有需要分析的源代码的文件夹,然后单击 OK(确定)。 注意:Fortify SCA 将 Build ID 设为文件夹名称。 |
系统会显示 AuditGuide Wizard(AuditGuide 向导) | 为需要审计的问题类型选择相关设置,然后单击 Run Scan(运行扫描)。 |
如果 Fortify SCA 在扫描源代码时遇到任何问题,即会显示 Warning(警告)对话框 | 单击 OK(确定) 继续。 Fortify SCA 会分析源代码。完成该过程之后,Audit Workbench 会显示 FPR 文件。 |
2、扫描其他项目
您可以使用“Advanced Scan(高级扫描)”向导转换和分析 Java、JavaScript、PHP、ASP、.NET 和 SQL 项目。对于源代码位于多个目录中、具有特殊转换或构建条件,或包含需要从项目中排除的文件的 Java 项目,应使用“Advanced Scan(高级扫描)”向导。
使用步骤 | 详细描述 |
扫描新项目 | 打开 Audit Workbench。 |
系统会显示开始页面 | 在“New Projects(新项目)”中,单击 Advanced Scan(高级扫描)。 |
系统会显示 Browse for Folder(浏览文件夹)对话框 | 选择项目所在的根目录,然后单击 OK(确定)。 |
会显示 Commandline Builder(命令行构建器)对话框。该向导会自动包含所有在扫描中支持的文件 | 另外,还可以添加其他目录中的文件: 单击 Add Directory(添加目录)。 |
命令行构建器
系统会显示 Browse to Folder(浏览至文件夹)对话框:
选择包含希望添加进行扫描的文件所在的文件夹。
单击 OK(确定)。
导航面板中将会显示该目录,并会自动添加所有受支持的文件以进行扫描。要删除该目录,右键单击相应的文件夹,然后选择 Remove Root(删除根):
另外,可排除文件或目录,例如测试源代码:
选择所需文件或目录。
右键单击并选择 Exclude(排除)。
该文件夹会变成灰色:
对于 Java 项目,设置以下内容:
选择包含类文件的文件夹,然后单击 Classpath Directory(类路径目录)。
在 Fortify SCA 转换阶段,该文件夹会变成蓝色,且文件会添加到类路径中:
选择项目的 Java 版本。
输入 Build ID。默认情况下,Build ID 为根目录。
输入 Fortify SCA 在分析阶段生成的 FPR 的路径和文件名。
单击 Next(下一步)。
系统会显示“Commandline Builder(命令行构建器)”对话框。
命令构建器
要跳过某一阶段,请取消勾选 Enable Clean(启用清除)、Enable Translation(启用转换)或 Enable Scan(启用扫描)复选框。
根据需要,修改每个 Fortify SCA 阶段的命令行选项:
Enable Clean(启用清除):
删除所有Fortify SCA与您在“Select Source(选择源)”对话框中设置的 Build ID 相关联的中间文件和内部版本记录。要更改 Build ID,请单击两次 Back(返回)。Enable Translation(启用转换):
创建Fortify SCA 中间文件并为项目指定 Build ID。Enable Scan(启用扫描):
分析源代码并创建可以在其中添加审计信息的 FPR 文件。
另外,还可以单击 Manage Rulepacks(管理规则包)以使用自定义规则包分析源代码,或者禁用某个安全编码规则包。系统会显示 AuditGuide Wizard(审计指南向导)。
审计指南向导
为希望进行审计的问题类型选择相关参数,然后单击 Next(下一步)。
单击 Run Scan(运行扫描)。
如果 Fortify SCA 在扫描源代码时遇到任何问题,会显示 Warning(警告) 对话框:
单击 OK(确定)继续。
Audit Workbench 会显示 FPR 文件。
1.3 审计分析结果
1、导航并查看分析结果
在您打开一个用来查看 Fortify Source Code Analyzer (Fortify SCA) 所检测到的问题的 Audit Workbench 项目之后,您可以在 Summary(摘要)面板中审计这些问题,以反映这些问题的严重级别,以及对该问题执行的安全分析状态。
系统会按审计结果的严重性以及准确性,对审计结果进行分组,并在默认情况下将问题识别为位于“Issues(问题)”面板中的 Hot(严重)列表内。单击 Warning(警告)和 Info(信息),查看分组在这些列表中的问题。
每个列表中的问题数量显示在相关按钮下面。
要检验与 Issues(问题)面板中所列问题相关的代码,选中该问题。源代码部分包含显示在源代码查看器面板中的问题,并在面板的标题中显示文件所包含问题的名称。
2、审计结果分析
本练习将会引导您浏览一下在练习 3 中使用 Fortify SCA 分析小程序产生的结果。请您检查 Fortify SCA 中各种不同分析器所发现的问题,并比较 Fortify SCA 生成的各种不同输出格式。
请考虑 UserServ.java 的内容:
检查语义问题:
下图突出显示了在练习 3 中的 UserServ.java 中检测出来的 password management 问题的各个要素。
唯一识别符:
开头的十六进制数(在此文本中以哈希标记 (#) 代替)是一个全局性唯一识别符,也称为实例识别符。这些识别符以分析器找到问题的路径、漏洞类型以及其他一些不受细小的代码变动影响的因素为基础而计算得出。例如,唯一识别符不依赖于代码行数。除了唯一性,实例识别符还提供了其他十分有价值的功能,它们可以标识出在多次分析中或不同代码版本之间的相同问题,因此可以被用来追踪审计过的问题。
严重性:
因为这个问题可以使得任何具有访问源代码或者字节码权限的人都可以操作数据库,所以 Fortify SCA 把它的严重级别设为 high。
漏洞类别/漏洞子类别:
Password Management 类中的问题都与潜在地不安全使用密码和其他证书有关。在这个例子中,问题归为 Hardcoded Password 子类别,是由于这里用来连接数据库的密码直接出现在了源代码中。
分析器:
问题由 semantic 分析器报告。semantic 分析器以与编译器十分相似的方式、以它定义的语义分析规则来检查代码。有关分析器的更多信息,请查看《Fortify Source Code Analyzer 用户指南》。
文件名/行号:
虽然密码本身出现在 UserServ.java 文件的第 18 行,但问题被报告在第 16 行,因为使用密码的函数在此被调用。
易受攻击的方法:
hardcoded password 被传递给了DriverManager 类中的 getConnection() 方法。
检查数据流问题。
使用下列图示来理解 SQL injection 问题
请注意这里有许多字段和上面的语义问题相同。这些字段的含意在数据流问题(和在下面要讨论的其他类型的问题)中是相同的。在这里,您将更加关注那些没有在语义问题中出现的字段。数据流问题比语义问题更复杂,因为它们牵涉到源代码中的多个地方。这是一个 SQL injection 问题,它发生在攻击者控制的数据被用来构造 SQL 查询时。数据流分析器将会跟踪那些潜在的恶意输入数据,从数据被输入到程序中一直到它被用来作为攻击的一部分。
Sink:
sink 的文件名、行号以及方法名指出了攻击者控制的查询在何处传递到数据库。紧随在行号之后而在类名和方法名之前的右箭头 (->) 指出了被感染的数据流入了 Statement.executeUpdate()。方法名后括号内的数字是这个方法的参数编号。数字 0 表示攻击者可以控制 executeUpdate() 的第一个参数(SQL 查询字符串)。
Pass-Through:
pass through 的文件名、行号以及变量名指出了将受感染的数据传递给 sink 的变量。双向箭头 (<=>) 指出了受感染的数据流入、并且流出该方法的变量 this.query。
Source:
source 的文件名、行号以及方法名指出了攻击者控制的数据第一次进入程序的位置。紧随在行号之后而在类名和方法名之前的左箭头 (<-) 指出 ServletRequest.getParameter() 引入了被感染的数据。方法名后括号中的单词 return 表示是该方法的返回值持有被感染的数据。
检查控制流问题。
使用下列图示来理解 unreleased resource 问题
控制流问题看上去类似于数据流问题,因为它们通常包含多个结点,不同点在于这些结点所代表的操作步骤是不安全的。Control flow 漏洞被表述为一系列状态的转换。
开始状态/结束状态:
第一个状态转换项显示了从start状态到connection状态的转换发生在第16行。第二个状态转换项显示了从connection状态到end_of_scope状态的转换发生在第44行。
转换表达式:
转换表达式紧跟在 start 和 end 状态名称之后。它给出了触发转换的代码构成。从 start 到 connection 的转换由调用 getConnection() 引起。从 connection 到 end_of_scope 的转换由变量 conn 到达其域的终止处引起。
控制流分析器在代码中找到了一个路径,该路径因为没有调用 conn.close() 而导致数据库连接泄漏。虽然 conn.close() 在第 37 行进行了调用,但是由于第 35 行调用 stmt.close()有可能会抛出异常,所以该方法并不能保证对 conn.close()的调用总会执行。
检查第二种结构问题。
考虑以下 member field race condition 问题
虽然结构问题可能只是报告了一行源代码,但是有时候它们会包含一些与程序结构相关的上下文,以便精确地指出错误。在这个问题中,分析器考虑了成员变量在一个继承于 HttpServlet 的类中进行声明的情况。
生成 Fortify Project Results (FPR) 文件
Fortify SCA 不但能够生成可以直接被审计员阅读的输出,也能够生成一个 FPR 文件,此文件可以用 Audit Workbench 或者其他工具进行查看。
再次运行 Fortify SCA,但是这一次生成 FPR 输出。使用 -f 选项将输出发送给 FPR 文件,如下所示:
sourceanalyzer -f results.fpr UserServ.java
1.4 内存注意事项
默认情况下,Fortify SCA 最多可使用 600 MB 的内存。如果该内存仍不能满足分析某个特定代码库的需要,您可能需要在扫描阶段提供更多的内存。这可以通过在 sourceanalyzer 命令中使用 -Xmx 选项来实现。
例如,要让 Fortify SCA 可用的内存达到 1000 MB ,可在命令中包含 -Xmx1000M 选项。
注意:为 Fortify SCA 分配的内存不应高于计算机本身的可用内存,因为这样会降低性能。指导原则是在没有运行其他内存密集型程序的情况下,分配的内存不能超过 2/3 的可用物理内存。