正确检测整型溢出
在看过编译器的这些行为后,你应该会明白——“在整型溢出之前,一定要做检查,不然,就太晚了”。
我们来看一段代码:
1
2
3
4
5
void foo(int m, int n)
{
size_t s = m + n;
.......
}
上面这段代码有两个风险:1)有符号转无符号,2)整型溢出。这两个情况在前面的那些示例中你都应该看到了。所以,你千万不要把任何检查的代码写在 s = m + n 这条语名后面,不然就太晚了。undefined行为就会出现了——用句纯正的英文表达就是——“Dragon is here”——你什么也控制不住了。(注意:有些初学者也许会以为size_t是无符号的,而根据优先级 m 和 n 会被提升到unsigned int。其实不是这样的,m 和 n 还是signed int,m + n 的结果也是signed int,然后再把这个结果转成unsigned int 赋值给s)
比如,下面的代码是错的:
1
2
3
4
5
6
7
void foo(int m, int n)
{
size_t s = m + n;
if ( m>0 && n>0 && (SIZE_MAX - m
//error handling...
}
}
上面的代码中,大家要注意(SIZE_MAX – m SIZE_MAX呢?因为,如果 m + n 溢出后,就被截断了,所以表达式恒真,也就检测不出来了。另外,这个表达式中,m和n分别会被提升为unsigned。
但是上面的代码是错的,因为:
1)检查的太晚了,if之前编译器的undefined行为就已经出来了(你不知道什么会发生)。
2)就像前面说的一样,(SIZE_MAX – m
3)另外,SIZE_MAX是size_t的最大值,size_t在64位系统下是64位的,严谨点应该用INT_MAX或是UINT_MAX
所以,正确的代码应该是下面这样:
1
2
3
4
5
6
7
8
9
void foo(int m, int n)
{
size_t s = 0;
if ( m>0 && n>0 && ( UINT_MAX - m
//error handling...
return;
}
s = (size_t)m + (size_t)n;
}
在《苹果安全编码规范》(PDF)中,第28页的代码中:
如果n和m都是signed int,那么这段代码是错的。正确的应该像上面的那个例子一样,至少要在n*m时要把 n 和 m 给 cast 成 size_t。因为,n*m可能已经溢出了,已经undefined了,undefined的代码转成size_t已经没什么意义了。(如果m和n是unsigned int,也会溢出),上面的代码仅在m和n是size_t的时候才有效。
不管怎么说,《苹果安全编码规范》绝对值得你去读一读。
上溢出和下溢出的检查
前面的代码只判断了正数的上溢出overflow,没有判断负数的下溢出underflow。让们来看看怎么判断:
对于加法,还好。
1
2
3
4
5
6
7
8
9
10
11
#include
void f(signed int si_a, signed int si_b) {
signed int sum;
if (((si_b > 0) && (si_a > (INT_MAX - si_b))) ||
((si_b
/* Handle error */
return;
}
sum = si_a + si_b;
}
对于乘法,就会很复杂(下面的代码太夸张了):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void func(signed int si_a, signed int si_b)
{
signed int result;
if (si_a > 0) { /* si_a is positive */
if (si_b > 0) { /* si_a and si_b are positive */
if (si_a > (INT_MAX / si_b)) {
/* Handle error */
}
} else { /* si_a positive, si_b nonpositive */
if (si_b
/* Handle error */
}
} /* si_a positive, si_b nonpositive */
} else { /* si_a is nonpositive */
if (si_b > 0) { /* si_a is nonpositive, si_b is positive */
if (si_a
/* Handle error */
}
} else { /* si_a and si_b are nonpositive */
if ( (si_a != 0) && (si_b
/* Handle error */
}
} /* End if si_a and si_b are nonpositive */
} /* End if si_a is nonpositive */
result = si_a * si_b;
}