《C和C++安全编码》课程笔记——第五章 整数安全

一、整数安全导论

整数安全的核心矛盾在于:

  • 数学理想:整数在数学中是无限集合
  • 计算机现实:受限于硬件存储(通常4/8字节)和语言规范

典型问题场景:

  1. 隐式类型转换陷阱(如 signed/unsigned 混合运算)
  2. 边界条件失控(INT_MAX + 1)
  3. 存储截断错误(long → short)
  4. 平台差异问题(ILP32 vs LP64数据模型)

隐式转换陷阱:

 int a = -1;
 unsigned b = a; // 转为4294967295(32位系统)

未定义行为:

int a = INT_MAX;
int b = a + 1;

 

二、整数数据类型

类型体系对比

类型 范围(32位) 风险点
int -2³¹ ~ 2³¹-1 溢出为UB
unsigned 0 ~ 2³²-1 回绕(0-1=UINT_MAX)
char 实现定义符号性 需显式声明signed/unsigned
特性 有符号类型 无符号类型
表示范围 -2^(n-1) ~ 2^(n-1)-1 0 ~ 2^n-1
溢出行为 未定义行为(UB) 明确定义的回绕
典型用途 通用计算、错误码 位操作、数组索引
零值扩展 符号位扩展 零扩展

关键特性

  • 补码表示:唯一零值,硬件运算高效(现代计算机标准)
  • 特殊类型
    • size_t:足够大的无符号类型(sizeof返回值)
    • ptrdiff_t:指针差值的有符号类型
size_t len = sizeof(arr);
ptrdiff_t diff = p2 - p1;


 

三、整数转换 

转换规则(转换优先级)

  1. 整数提升
    • 若原始类型范围≤int→提升为int,否则为unsigned int
    • unsigned char a = 0xFF;
      int b = a; // 提升为int(255)
      
  2. 普通算术转换
    • 混合运算时按优先级转换(如signed+unsignedunsigned) 
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/-1 INT_MIN / -1(UB)

 

汇编级检测

  • 有符号溢出:检查OF标志位(jo指令)
  • 无符号回绕:检查CF标志位(jc指令)

 

五、整数漏洞 

漏洞模式

  1. 缓冲区溢出(45%):
    int buf[10];
    buf[index] = x; // index未验证范围
    
  2. 内存分配错误(15%):
    size_t size = x * y;
    malloc(size);   // 乘法可能回绕
    
  3. 逻辑错误(30%):
    for (unsigned i = 5; i >= 0; i--) // 死循环(回绕)
    

 

真实案例

  • CVE-2021-3156(sudo堆溢出)
    size_t len = strlen(name);
    char *buf = malloc(len + 2); // len+2可能回绕
    

六、缓解策略

防御技术栈

层级 措施
代码层面 使用安全库(如
编译层面 -ftrapv(有符号溢出陷阱)
运行时 ASLR、边界检查器
硬件 MPU内存保护单元

《C和C++安全编码》课程笔记——第五章 整数安全_第1张图片 

安全编码清单

  1. 所有外部输入验证范围
  2. 禁止隐式signed/unsigned混用
  3. 数组索引显式检查上下界
  4. 内存分配前验证计算无溢出
  5. 启用编译选项(-Wall -Wextra
int a = -5;   
unsigned b = 10;   
long c = a + b; // a转为UINT_MAX-4,结果可能非预期

回绕检测:

if (UINT_MAX - a < b) { }

你可能感兴趣的:(c语言,c++,笔记)