7 数字操作
7.1 【必须】防止整数溢出
在计算时需要考虑整数溢出的可能,尤其在进行内存操作时,需要对分配、拷贝等大小进行合法校验,防止整数溢出导致的漏洞。
错误(该例子在计算时产生整数溢出)
const kMicLen = 4;
// 整数溢出
void Foo() {
int len = 1;
char payload[10] = { 0 };
char dst[10] = { 0 };
// Bad, 由于len小于4字节,导致计算拷贝长度时,整数溢出
// len - MIC_LEN == 0xfffffffd
memcpy(dst, payload, len - kMicLen);
}
正确例子
void Foo() {
int len = 1;
char payload[10] = { 0 };
char dst[10] = { 0 };
int size = len - kMicLen;
// 拷贝前对长度进行判断
if (size > 0 && size < 10) {
memcpy(dst, payload, size);
printf("memcpy good\n");
}
}
关联漏洞:
高风险-内存破坏
7.2 【必须】防止Off-By-One
在进行计算或者操作时,如果使用的最大值或最小值不正确,使得该值比正确值多1或少1,可能导致安全风险。
错误:
char firstname[20];
char lastname[20];
char fullname[40];
fullname[0] = '\0';
strncat(fullname, firstname, 20);
// 第二次调用strncat()可能会追加另外20个字符。如果这20个字符没有终止空字符,则存在安全问题
strncat(fullname, lastname, 20);
正确:
char firstname[20];
char lastname[20];
char fullname[40];
fullname[0] = '\0';
// 当使用像strncat()函数时,必须在缓冲区的末尾为终止空字符留下一个空字节,避免off-by-one
strncat(fullname, firstname, sizeof(fullname) - strlen(fullname) - 1);
strncat(fullname, lastname, sizeof(fullname) - strlen(fullname) - 1);
对于 C++ 代码,再次强烈建议使用 string
、vector
等组件代替原始指针和数组操作。
关联漏洞:
高风险-内存破坏
7.3 【必须】避免大小端错误
在一些涉及大小端数据处理的场景,需要进行大小端判断,例如从大段设备取出的值,要以大段进行处理,避免端序错误使用。
关联漏洞:
中风险-逻辑漏洞
7.4 【必须】检查除以零异常
在进行除法运算时,需要判断被除数是否为零,以防导致程序不符合预期或者崩溃。
错误:
double divide(double x, double y) {
return x / y;
}
int divide(int x, int y) {
return x / y;
}
正确:
double divide(double x, double y) {
if (y == 0) {
throw DivideByZero;
}
return x / y;
}
关联漏洞:
低风险-拒绝服务
7.5 【必须】防止数字类型的错误强转
在有符号和无符号数字参与的运算中,需要注意类型强转可能导致的逻辑错误,建议指定参与计算时数字的类型或者统一类型参与计算。
错误例子
int Foo() {
int len = 1;
unsigned int size = 9;
// 1 < 9 - 10 ? 由于运算中无符号和有符号混用,导致计算结果以无符号计算
if (len < size - 10) {
printf("Bad\n");
} else {
printf("Good\n");
}
}
正确例子
void Foo() {
// 统一两者计算类型为有符号
int len = 1;
int size = 9;
if (len < size - 10) {
printf("Bad\n");
} else {
printf("Good\n");
}
}
关联漏洞:
高风险-内存破坏
中风险-逻辑漏洞
7.6 【必须】比较数据大小时加上最小/最大值的校验
在进行数据大小比较时,要合理地校验数据的区间范围,建议根据数字类型,对其进行最大和最小值的判断,以防止非预期错误。
错误:
void Foo(int index) {
int a[30] = {0};
// 此处index是int型,只考虑了index小于数组大小,但是并未判断是否大于0
if (index < 30) {
// 如果index为负数,则越界
a[index] = 1;
}
}
正确:
void Foo(int index) {
int a[30] = {0};
// 判断index的最大最小值
if (index >=0 && index < 30) {
a[index] = 1;
}
}
关联漏洞:
高风险-内存破坏