一、整数安全导论
整数安全的核心抵牾在于:
- 数学理想:整数在数学中是无限集合
- 计算机现实:受限于硬件存储(通常4/8字节)和语言规范
典型问题场景:
- 隐式类型转换陷阱(如 signed/unsigned 混合运算)
- 边界条件失控(INT_MAX + 1)
- 存储截断错误(long → short)
- 平台差别问题(ILP32 vs LP64数据模型)
隐式转换陷阱:
- int a = -1;
- unsigned b = a; // 转为4294967295(32位系统)
复制代码 未定义行为:
- int a = INT_MAX;
- int b = a + 1;
复制代码
二、整数数据类型
类型体系对比
类型范围(32位)风险点int-2³¹ ~ 2³¹-1溢出为UBunsigned0 ~ 2³²-1回绕(0-1=UINT_MAX)char实现定义符号性需显式声明signed/unsigned 特性有符号类型无符号类型表树模围-2^(n-1) ~ 2^(n-1)-10 ~ 2^n-1溢出行为未定义行为(UB)明白定义的回绕典型用途通用计算、错误码位操作、数组索引零值扩展符号位扩展零扩展 关键特性
- 补码表示:唯一零值,硬件运算高效(当代计算机尺度)
- 特别类型:
- size_t:足够大的无符号类型(sizeof返回值)
- ptrdiff_t:指针差值的有符号类型
- size_t len = sizeof(arr);
- ptrdiff_t diff = p2 - p1;
复制代码
三、整数转换
转换规则(转换优先级)
- 整数提升:
- 若原始类型范围≤int→提升为int,否则为unsigned int
- unsigned char a = 0xFF;
- int b = a; // 提升为int(255)
复制代码
- 普通算术转换:
- 混合运算时按优先级转换(如signed+unsigned→unsigned)
- int a = -5;
- unsigned b = 10;
- long c = a + b; // a转为UINT_MAX-4,结果可能非预期
复制代码
隐式转换风险矩阵
转换方向潜在风险示例有符号→无符号负值变极大正值int(-1) → unsigned(4294967295)宽类型→窄类型高位截断long(0x12345678) → short(0x5678)浮点→整数未定义行为(UB)int x = 1e30;- unsigned char a = 0xFF; // 255
- unsigned char b = 0x01; // 1
-
- // 情况1:直接比较(无提升)
- if (a < b) { // 比较的是 255 < 1 → false
- // 不会执行
- }
-
- // 情况2:提升为 int 后比较
- if ((int)a < (int)b) { // 比较的是 255 < 1 → false(和情况1一样)
- // 不会执行
- }
-
- // 情况3:危险的情况(涉及有符号和无符号混合运算)
- signed char c = -1; // -1(有符号)
- if (a < c) { // 提升为 int 后比较:255 < -1 → false(但可能不符合预期)
- // 不会执行
- }
复制代码- unsigned char a = 0xFF; // 255
- signed char b = -1; // -1
-
- // 开发者可能误以为比较的是 (unsigned char)-1 == 255
- if (a < b) {
- printf("a < b\n"); // 不会执行,因为实际比较的是 255 < -1(false)
- } else {
- printf("a >= b\n"); // 会执行
- }
-
- // 如果确实想比较回绕后的值,应该显式转换:
- if (a < (unsigned char)b) { // 比较 255 < 255 → false
- printf("a < (unsigned char)b\n");
- }
复制代码
四、整数操作
安全范式
- _Bool safe_add(int a, int b, int *res) {
- if ((b > 0 && a > INT_MAX - b) ||
- (b < 0 && a < INT_MIN - b)) {
- return 0;
- }
- *res = a + b;
- return 1;
- }
复制代码 操作安全检测方法伤害案例加法if (a > INT_MAX - b) /*溢出*/INT_MAX + 1(UB)乘法if (a > INT_MAX / b) /*溢出*/INT_MIN * -1(UB)移位右操作数必须∈[0,类型宽度-1]1 << 32(UB)除法检查除零和INT_MIN/-1INT_MIN / -1(UB)
汇编级检测
- 有符号溢出:检查OF标志位(jo指令)
- 无符号回绕:检查CF标志位(jc指令)
五、整数毛病
毛病模式
- 缓冲区溢出(45%):
- int buf[10];
- buf[index] = x; // index未验证范围
复制代码 - 内存分配错误(15%):
- size_t size = x * y;
- malloc(size); // 乘法可能回绕
复制代码 - 逻辑错误(30%):
- for (unsigned i = 5; i >= 0; i--) // 死循环(回绕)
复制代码
真实案例
- CVE-2021-3156(sudo堆溢出):
- size_t len = strlen(name);
- char *buf = malloc(len + 2); // len+2可能回绕
复制代码
六、缓解策略
防御技能栈
层级措施代码层面使用安全库(如<stdckdint.h>)编译层面-ftrapv(有符号溢出陷阱)运行时ASLR、边界检查器硬件MPU内存保护单位
安全编码清单
- 所有外部输入验证范围
- 克制隐式signed/unsigned混用
- 数组索引显式检查上下界
- 内存分配前验证计算无溢出
- 启用编译选项(-Wall -Wextra)
- int a = -5;
- unsigned b = 10;
- long c = a + b; // a转为UINT_MAX-4,结果可能非预期
复制代码 回绕检测:
- if (UINT_MAX - a < b) { }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |