NNTS.MIGHT
可能存在的非 Null 结尾字符串导致的缓冲区溢出
在 C 和 C++ 中,C 字符串或 Null 结尾的字符串是以空字符 (\0) 结尾的字符序列。C 字符串的长度通过搜索空字符来确定。
NNTS 系列检查器会查找那些使用了带有并非或可能并非以 Null 终止的字符数组的字符串操纵函数的代码。NNTS.MIGHT 检查器会查找这样的代码:其使用字符串操纵函数,但无法对函数中的必要信息进行评估,例如带有未知长度字符的参数。
漏洞与风险
Null 结尾曾经导致过安全性问题。例如:
- 在字符串中插入空字符可能导致意外地将其截断
- 没有为空字符分配足够的空间或忘记空字符的存在是很常见的 bug
- 许多程序在将字符串复制到固定大小的缓冲区之前都不会检查长度,当字符串太长时,就会导致缓冲区溢出
- 无法保存空字符就意味着字符串和二进制数据需要由不同的函数来处理,如果使用了错误的函数就可能导致问题
缓解与预防
为了避免问题:
- 如果性能约束条件允许,可添加特殊的代码来验证字符串缓冲区是否以 Null 结尾
- 切换到有界字符串操纵函数,比如 strncpy
- 检查缓冲区溢出引用通告中所涉及的缓冲区长度
漏洞代码示例
复制
int nnts_m_m_2(char * src)
{
char buf[8];
char tgt[1024];
strncpy(buf, src, 3);
strcpy(tgt, buf);
return 0;
}
在本示例中,Klocwork 将在第 7 行生成 NNTS.MIGHT 缓冲区溢出问题报告。参数 src 的字符串长度未知,并且如果 src 的字符串长度等于或大于三,则在第 6 行对 strncpy 的调用将仅向 buf 中写入三个字符。如果分配了 buf 的堆栈内存中没有 Null 结尾,则第 7 行的调用将访问其边界以外的数组。如果 buf 中有 Null 结尾,strcpy 可能仍会将未初始化的数据复制到 tgt 缓冲区。该代码可能导致缓冲区溢出,并造成各种严重的问题。
如果 src 的字符长度已知等于或超过三,Klocwork 将生成 NNTS.MUST 问题报告。
修正代码示例
复制
int nnts_m_m_2(char * src)
{
char buf[8];
char tgt[1024];
strncpy(buf, src, 3);
// ensure null termination
buf[3] = '\0';
strcpy(tgt, buf);
return 0;
}
在修正代码示例中,在第 8 行明确地向 buf 添加了一个 Null 结尾字符。由于 buf 不再是一个以非 Null 结尾的字符串,因此在第 9 行就避免了该问题。
相关检查器
外部指导
- CERT ARR00-C:了解数组的工作原理
- CERT ARR30-C:不形成或使用超出边界的指针或数组下标
- CERT STR03-C:请勿无意中截断字符串
- CERT STR32-C:请勿将非 Null 结尾的字符序列传递给需要字符串的库函数
- CERT STR50-CPP:确保字符串存储器具有足够的空间存储字符数据和 Null 终止符
- CWE-119:未正确地将操作限制在内存缓冲区边界内
- CWE-120:复制缓冲区而不检查输入大小(“典型的缓冲区溢出”)
- CWE-125:超出边界的读取
- CWE-170:不正确的 Null 结尾
- CWE-787:超出边界的写入
- STIG-ID:APP3590.1 应用程序易受缓冲区溢出影响
扩展
此检查器可进行扩展。有关详情,请参阅调整 C/C++ 分析。