【缺陷周话】第35期:除数为零
字数 1155 2025-08-18 11:38:37
除数为零缺陷分析与防范指南
1. 缺陷概述
除数为零(Divide By Zero)是C/C++编程中常见的安全缺陷,属于CWE-369分类。当程序执行除法或取模运算时,如果除数为零,会导致程序崩溃或产生未定义行为。
基本算数运算符
+加法-减法*乘法/除法%取模
安全要求:除法(/)和取模(%)运算的第二个操作数绝对不能为零。
2. 危害分析
主要影响
- 程序崩溃
- 拒绝服务(DoS)漏洞
- 可能导致程序进入未定义状态
实际漏洞案例
-
ImageMagick (CVE-2019-11472)
- 影响版本:7.0.8-41 Q16
- 漏洞位置:XWD图像解析组件的
ReadXWDImage函数 - 攻击方式:特制XWD图像文件可导致除零漏洞
-
Wireshark (CVE-2018-19628)
- 影响版本:2.6.0-2.6.4, 2.4.0-2.4.10
- 漏洞位置:ZigBee ZCL解析器
- 攻击方式:畸形数据包导致崩溃
-
elfutils (CVE-2018-18521)
- 影响版本:0.174
- 漏洞位置:
arlibaddsymbols()函数 - 问题原因:未正确处理shentsize为零的情况
3. 缺陷代码示例
#include <stdio.h>
void badSink(int data)
{
/* POTENTIAL FLAW: Possibly divide by zero */
int result = 100 / data;
printf(result);
}
void main()
{
int data;
/* Initialize data */
data = -1;
/* POTENTIAL FLAW: Read data from the console using fscanf() */
fscanf(stdin, "%d", &data);
badSink(data);
}
问题分析
- 第28行使用
fscanf()从标准输入读取数据到data - 输入来自不可信源,可能为零
- 第30行进行除法运算时未检查除数是否为零
4. 修复方案
修复代码示例
#include <stdio.h>
void goodSink(int data)
{
/* FIX: test for a zero denominator */
if(data != 0)
{
int result = 100 / data;
printf(result);
}
else
{
printf("Error: Attempt to divide by zero");
}
}
void main()
{
int data;
/* Initialize data */
data = -1;
/* POTENTIAL FLAW: Read data from the console using fscanf() */
fscanf(stdin, "%d", &data);
goodSink(data);
}
修复要点
- 在执行除法前检查除数是否为零
- 为零时提供错误处理路径
- 使用明确的错误提示信息
5. 防范措施
最佳实践
- 输入验证:对所有外部输入进行验证,确保除数不为零
- 防御性编程:在除法运算前添加显式检查
- 异常处理:为零时提供适当的错误处理逻辑
- 代码审查:重点关注以下场景:
- 除数为函数返回值
- 除数为复杂表达式结果
- 除数为外部输入数据
- 静态分析:使用代码卫士等工具进行自动化检测
高风险场景
- 除数为用户输入数据
- 除数为网络数据解析结果
- 除数为文件读取内容
- 除数为复杂计算表达式结果
- 除数为可能溢出的变量
6. 检测与验证
静态分析工具
- 使用代码卫士等专业工具检测
- 检测等级:高
测试用例设计
- 边界值测试:输入0值
- 异常输入测试:输入可能产生零的表达式
- 模糊测试:随机生成输入数据
7. 相关资源
- CWE-369: Divide By Zero (3.2)
- Samate Juliet Test Suite for C/C++ v1.3
- 代码卫士静态分析工具
8. 总结
除数为零是常见但严重的安全缺陷,可能导致程序崩溃和拒绝服务。通过输入验证、防御性编程和静态分析工具的综合应用,可以有效预防此类缺陷。开发人员应特别注意来自不可信源的除数,并在所有除法运算前添加显式检查。