C 和 C++ 编程中的整数溢出

来源:互联网 发布:小青柑淘宝店导航条 编辑:程序博客网 时间:2024/05/01 04:47
 
一、 超越整数范围的溢出
当执行算法以计算缓冲区大小,真正的安全性漏洞会随同这些缺陷一起出现。请看下面的示例:
void func(char *b1, int c1, char *b2, int c2) {
    const int MAX = 48;
    if (c1 + c2 > MAX) return;
    char *pBuff = new char[MAX];
    memcpy(pBuff,b1,c1);
    memcpy(pBuff+c1,b2,c2);
}
上面的代码看起来没有问题,但如果将 c1 和 c2 相加,结果超过 232-1,您就会意识到有问题了。例如,0xFFFFFFF0 和 0x40 相加的结果为 0x30(十进制为 48)。当这些值用于 c1 和 c2 时,加起来的和可以通过大小检查,然后代码会将大约 4GB 复制到 48 个字节的缓冲区。这样就会出现缓冲区溢出!类似于这样的许多错误都可以被利用,使攻击者将代码注入您的进程中。
 
那像这种问题如何解决呢?
可以在可能发生溢出的地方,加上条件判断,比如解决上面的问题, 可以写为:
If( c1 < 0 || c2 < 0 || c1 + c2 > MAX ) return;
 
二、 类型转换时的溢出
 
在一个复杂的应用程序中,一个表达式中包含了各种类型的变量,这时在运算过程中就包括了各种的默认类型转换。这个默认类型转换都是按你想的过程进行的吗? 先看如下的例子, 首先, 自己猜测一下结果:
#include <stdio.h>
 
int main()
{
    signed short intb1, intb2;
    unsigned short uintb;
    long longc1, longc2;
    unsigned long ulongc1, ulongc2;
   
    longc1 = -34;
    ulongc1 = 7;
    uintb   = 3;
    intb1   = -84;
 
    if( longc1 > ulongc1 )
        printf("%ld > %lu /n", longc1, ulongc1);
    else
        printf("%ld < %lu /n", longc1, ulongc1);
   
    if( longc1 > uintb )
        printf("%ld > %u /n", longc1, uintb);
    else
        printf("%ld < %u /n", longc1, uintb);
 
    if( intb1 > uintb )
        printf(" %d > %u /n", intb1, uintb);
    else
        printf(" %d < %u /n", intb1, uintb);
 
 
    longc2 = ulongc1 + longc1;
    printf("%lu + %ld = %ld /n", ulongc1, longc1, longc2);
 
    ulongc2 = ulongc1 + longc1;
    printf("%lu + %ld = %lu /n", ulongc1, longc1, ulongc2);
 
    if( ulongc1 + longc1 > 0)
        printf("%lu + %ld > 0 /n", ulongc1, longc1);
}输出的结果是:
-34 > 7
-34 < 3
 -84 < 3
7 + -34 = -27
7 + -34 = 4294967269
7 + -34 > 0
以上的程序在GCC2.9 上编译,liunx7.3上运行的结果。
从输出的结果来看,和你预期的输出结果一样吗?
也许从下面的输出结果,你能了解到问题出在那里了吗?
7 + -34 = -27
7 + -34 = 4294967269
7 + -34 > 0
是的,从中我们就可以看出, 类型转换的作用。
现在来解释一下如下表示式:
7 + -34 > 0
因为 ulongc1 是无符号数, longc1是有符号数 ,所以longc1转换为无符号相加,产生如下结果:
ulongc1 + longc1 = 4294967269
这时,自然
ulongc1 + longc1 > 0
 
对于longc1 > uintb 以及 intb1 > uintb 的比较过程中, 默认的转换中,都将双字节short类型(不管是有无符号)先转换为有符号四字节类型int , 再进行比较,所以才比较出正确的结果。
 
从以上的测试来看, 我们来看如下一段介绍默认类型转换的:
 
C语言中,在进行表达式计算时,如果其中有多种类型时,一般先进行类型转换,再进行计算。默认的类型转换是从字节少向字节多的转换,有符号的向无符号类型转换。因些,如果算术运算符的一个操作数是有符号整数,另一个是无符号整数,那么有符号整数会被转换为无符号整数,计算结果自然也是无符号数。
 
发现其中也有不完善的地方, 因为低于四字节的类型,如char , unsigned char , short, unsigned shourt 都是先转换为int 类型,再进行计算, 数据没有丢失及溢出,因此不存在判断错误。所以要注意的就是int 以及 unsigned int混合,此时何正确进行判断呢?
只要先有符号数的范围检查:
if ((longc1 > 0)&& (longc1 > ulongc1))
{
     // longc1 > ulongc1
}