【缺陷周话】第20期:无符号整数回绕
字数 1375 2025-08-18 11:37:53
无符号整数回绕安全缺陷详解
1. 无符号整数回绕概述
无符号整数回绕是C/C++编程中常见的安全缺陷,当无符号整数的数值超过其取值范围时会发生回绕现象,而不是产生溢出错误。
1.1 无符号整数取值范围
ANSI标准定义的无符号整数类型及范围:
| 类型 | 位数 | 取值范围 |
|---|---|---|
| unsigned int | 16/32 | 0~65535 |
| unsigned short int | 16 | 0~65535 |
| unsigned long int | 32 | 0~4294967295 |
| unsigned long long int | 64 | 0~2^64-1 |
1.2 回绕原理
根据C99标准第6.2.5节第9条规定:
- 涉及无符号整数的计算不会产生溢出
- 当数值超过无符号整数的取值范围时会发生回绕
- 最大值加1会返回0
- 最小值减1会返回该类型的最大值
可能导致回绕的操作符:+, -, *, ++, --, +=, -=, *=
2. 无符号整数回绕的危害
2.1 直接危害
- 产生不符合预期的数值错误
- 可能导致内存操作异常
2.2 典型利用场景
-
内存拷贝函数:如
memcpy(),当回绕产生极大值时,可能导致:- 复制巨大数据量
- 破坏堆栈结构
-
内存分配函数:如
malloc(),当参数回绕时可能导致:- 分配0长度内存
- 内存分配失败
2.3 实际漏洞案例
| CVE编号 | 描述 |
|---|---|
| CVE-2018-6323 | BFD库(libbfd)中elf_object_p()函数存在无符号整数回绕,精心制作的ELF文件可导致拒绝服务 |
| CVE-2018-5848 | Android Qualcomm WIGIG组件因未正确处理无符号整数回绕导致缓冲区溢出,可执行任意代码或拒绝服务 |
3. 示例代码分析
3.1 缺陷代码示例
void badSink(unsigned int data)
{
unsigned int result = data * 2; // 可能发生回绕
printUnsignedLine(result);
}
void bad()
{
unsigned int data;
data = 0;
fscanf(stdin, "%u", &data); // 从输入读取无符号整数
badSink(data);
}
问题分析:
- 从输入流读取无符号整数
data - 只对下限进行了限制(0),未限制上限
- 当
data*2超过UINT_MAX时会发生回绕
3.2 修复代码示例
void goodSink(unsigned int data)
{
if(data > (UINT_MAX/2)) // 检查是否会导致回绕
{
printLine("Input value is too large to perform arithmetic safely.");
}
else
{
unsigned int result = data * 2;
printUnsignedLine(result);
}
}
void good()
{
unsigned int data;
data = 0;
fscanf(stdin, "%u", &data);
goodSink(data);
}
修复要点:
- 在执行乘法前检查
data是否超过UINT_MAX/2 - 如果可能回绕,则拒绝执行并提示错误
4. 防御措施
4.1 编码实践
-
参数验证:
- 对无符号整数参数进行上下界检查
- 特别注意来自不可信源的输入数据
-
运算前检查:
- 在执行运算前检查是否会导致回绕
- 例如乘法前检查
a > MAX/b
-
使用安全库:
- 使用经过安全验证的数学运算库
- 如
SafeInt等安全整数运算库
4.2 检测方法
-
静态代码分析:
- 使用专业工具(如360代码卫士)进行自动化检测
- 识别潜在的无符号整数回绕风险
-
代码审查:
- 重点关注无符号整数运算
- 特别审查内存分配和拷贝相关代码
5. 总结
无符号整数回绕是C/C++开发中常见但容易被忽视的安全问题,可能导致严重的安全漏洞。开发者应当:
- 充分理解无符号整数的回绕特性
- 对所有无符号整数运算进行边界检查
- 对不可信输入进行严格验证
- 使用自动化工具辅助检测
通过规范的编码实践和严格的代码审查,可以有效避免无符号整数回绕导致的安全问题。