ABV.GENERAL
缓冲区溢出 — 数组索引超出边界
缓冲区溢出或溢出是一种异常现象,其中将数据写入缓冲区的程序会溢出缓冲区的边界并覆盖相邻的内存。通常,当一个程序将字符串复制到缓冲区时,便会发生这种问题。
C 和 C++ 不提供任何内置保护功能来阻止访问或覆盖内存任何部分中的数据,也不自动检查写入某一数组(此语言的内置缓冲区类型)的数据是否在该数组的边界内。
ABV.GENERAL 检查器是一种通用检查器,用于查找数组边界违规情况,即对超出该数组边界的数组元素的任何访问。
漏洞与风险
旨在执行代码或改变程序操作方式的输入可触发缓冲区溢出。这可能导致程序行为不稳定,包括内存访问错误、错误的结果、故障或系统安全漏洞。
缓冲区溢出的后果包括有效数据被覆盖以及执行任意和潜在恶意代码。例如,缓冲区溢出可通过以下三种方式操纵程序:
- 覆盖内存中缓冲区附近的局部变量,以更改程序的行为使之有利于攻击者
- 覆盖堆栈帧中的返回地址,以便在攻击者指定的返回地址(通常为用户输入填充的缓冲区)处恢复执行
- 覆盖随后将执行的函数指针或异常处理程序
漏洞代码示例 1
int main()
{
char fixed_buf[10];
sprintf(fixed_buf,"Very long format string\n"); // Line 4.ABR
return 0;
}
Klocwork 为第 4 行生成了一个缓冲区溢出报告,指出 fixed_buf 的数组索引可能超出边界:大小为 10 的数组 fixed_buf 可以使用索引值 0 至 24。ABR 检查器可查找数组边界违规情况,在本例中,其将查找对超出该数组边界的数组元素 fixed_buf 的访问。
修正代码示例 1
int main()
{
char fixed_buf[10];
snprintf(fixed_buf, sizeof(fixed_buf), "Very long format string\n");
return 0;
}
在该修正代码示例中,sprintf 函数(该函数假设输出缓冲区的大小足以容纳产生的字符串)已被 snprintf 函数替代(该函数会将最大字节数写入缓冲区)。请注意,这只是本示例代码中阻止缓冲区溢出的其中一种方法,此修正代码将导致字符串截断,这可能需要加以说明,具体取决于应用程序。
漏洞代码示例 2
void foo()
{
char a[8]; // holds two 4-byte ints
for (int i = 0; i < sizeof(a); i++)
{
((int*)a)[i] = i;
}
}
有时,数组或指向已分配缓冲区的指针会在访问其元素前转换为不同类型。在本示例中,第 4 行循环的上边界视为数组 a 的大小(以字符表示),但是数组 a 在第 6 行作为一个整数数组进行访问。Klocwork 为上文的代码段生成了一个缓冲区溢出报告,指出 a 的数组索引可能由于第 6 行 1 个字节的字符与 4 个字节的整数之间的差异而超出边界:大小为 2 的数组 a 可以使用索引值 2 至 7。该问题的回溯所包含的信息指出,声明为 char[8] 的数组 a 被视作大小为 2 的数组。
修正代码示例 2
void foo()
{
char a[8]; // holds two 4-byte ints
for (int i = 0; i < sizeof(a) / sizeof(int); i++)
{
((int*)a)[i] = i;
}
}
在该修正代码示例中,第 4 行循环的上边界已更改为查看被视作整数数组的数组 a 中的元素数量。
相关检查器
- ABV.ANY_SIZE_ARRAY
- ABV.GENERAL.MULTIDIMENSION
- ABV.ITERATOR
- ABV.MEMBER
- ABV.STACK
- ABV.TAINTED
- ABV.UNICODE.BOUND_MAP
- ABV.UNICODE.FAILED_MAP
- ABV.UNICODE.NNTS_MAP
- ABV.UNICODE.SELF_MAP
- ABV.UNKNOWN_SIZE
外部指导
- CERT ARR00-C:了解数组的工作原理
- CERT ARR30-C:不形成或使用超出边界的指针或数组下标
- CERT ARR38-C:保证库函数不形成无效指针
- CERT CTR50-CPP:保证容器索引和迭代器在有效范围内
- CERT ENV01-C:不作出关于环境变量大小的假设
- CERT EXP08-C:确保指针算术运算使用正确
- CERT POS30-C:适当使用 readlink() 函数
- CWE-119:未正确地将操作限制在内存缓冲区边界内
- CWE-120:复制缓冲区而不检查输入大小(“典型的缓冲区溢出”)
- CWE-122:基于堆的缓冲区溢出
- CWE-124:缓冲区欠载(“缓冲区下溢”)
- CWE-125:超出边界的读取
- CWE-127:缓冲区读取不足
- CWE-193:差一错误
- CWE-251:无
- CWE-786:访问缓冲区开始之前的内存位置
- CWE-787:超出边界的写入
- CWE-788:访问缓冲区末尾之后的内存位置
- CWE-805:访问长度值错误的缓冲区
- CWE-806:使用源缓冲区的大小访问缓冲区
扩展
此检查器可通过 Klocwork 知识库进行扩展。有关详情,请参阅调整 C/C++ 分析。