zoukankan      html  css  js  c++  java
  • C++关于类型及运算的安全问题

    某些C/C++书籍教会了我们几十年前流行的写法,浑然不知的我们理所当然的写出这些代码,然后把当年造就了无数漏洞的恶魔重新放入新的体系里。
    在理想情况下,对类型的错误应用会导致一些错误,并让我们第一时间发觉;在最糟的情况下,其错误在很久之后才被发现,而且那时我们的系统已经遭受了足够多的攻击。
    几个基本的类型规则:
    1.类型转换规则:
    系统将精度高低规定如箭头所示。
    char,short -> int -> unsigned -> long ->double <- float
    A.在运算中的自动转换:
    1. 任何两个长度低于int类型的值运算时必然转换为int类型(包括==、>=等逻辑判断)
    2. 等级等于或高于int的同类型运算时,仍为原类型
    3. 两个不同类型数据参与运算时,两个数据都转换为其中较高级别的类型(两个值都长度低于int则遵从第一条)
    4. 对于表达式中的常数,默认为int类型,如果该值为正数且超过INT_MAX(在头文件 limits.h中),则默认为 unsigned int (源文件中一般不允许常数超过long,故不讨论更大的数值)。
    5. 如果一个值为64位,则另一个也会向上转换为64位,无符号的64位值是这些64位值的上限。

    B.强制类型转换:
    1.强制类型转换不对目标变量进行直接的转换,而是产生了一个中间量

    C.Notice:
    1.在类型转换中,如果将长类型转换为短类型,则将其截断。
    2.对于短类型转换为长类型:
    a.无符号类型转换为符号类型,中间不发生符号拓展(原本就没有符号)
    b.符号类型向一个更长类型转换,如果符号类型符号位为1(对大部分系统而言为负数),则在长类型中多出的空位中补充1

    示例(全部假定为32位机):
    1.一个条件判断中的类型转换问题
    int flagA =0x7f;
    char flagB =0x80;
    if( (char)(flagA ^ flagB) == 0xff)
    printf("Worked!");

    真实执行情况是:
    根据A-3,flagB转换为int类型;
    根据C-2-b,flagB发生符号拓展。

    flagA =0x0000007f;
    flagB =0xffffff80;
    flagA ^ flagB =0xffffffff
    强制类型转换之后,发生截断,于是左侧值此时为 0xff(char)
    根据A-4,0xff默认视为int类型,因此0xff(char)需要转换为int类型才能进行 ==比较。
    0xff(char)转换后,经过符号拓展,结果为 0xffffffff(int)
    故示例表达式始终为假。

    PS:本例出自《软件安全的24宗罪——编程缺陷与修复之道》(清华大学出版社,2010.6) P106,本例中略有改动。原例中条件表达式有误。译文为 if( (char) flags ^ LowByte == 0xff),这个表达式应该是漏掉了在 flags ^ LowByte外加一层括号,这种写法不会导致符号拓展(至少根据目前的C++语法是这样),最终条件判断通过,有兴趣的可以试一试。
    运算
    在数值运算中,主要考虑其二进制变化即可。
    A.对于加减运算,需要记住的是:
    N位无符号数从0-2^N-1变化,N位有符号数从 -2^(N-1) - 2^(N-1)-1,如果加减运算超过上限或下限,则可以将数值范围视为一个环来推算。
    B.对于乘法运算:
    1.无符号数的乘法结果超过 UINT_MAX( limits.h )时,即使使用一个足够长的类型接收结果,结果都是错的 【在G++4.4.3中验证】
    对于这种情况,一种稳妥的检测方法是检验 b>UINT_MAX/a,更有效的方法是将结果存放在一个更大的整数中,查看是否存在溢出
    PS.《软件安全的24宗罪》P105中原文为 a*b>MAX_INT,不知道原作者的表达式是否跟MS编译器有关,或者另有所指。
    2.两个无符号短整数相乘,根据之前所述的类型转换原则,两个数最终会得到一个整型数
    3.有符号数的乘法应当做额外检查,以确保结果的符号没有变 【未能写出验证】
    C.对于无符号32位或者64位整数与带负号的整数之间的运算,及短类型负数(如8位整数-1)与整数的运算,注意类型转换会导致出现意料之外的结果。
    例如 -1/UINT_MAX =1; 在该运算中,-1被视为整型数,运算中提升为无符号整数,其值为0xffffffff,与UINT_MAX相等,故结果为1.
    此外,取模运算中返回值的符号依赖于具体的实现方案,对于 -1%4,有的得到1,有的得到-1
    D.对于比较运算,如果使用有符号数,首先确保待比较的数大于或等于0,然后确定它比上限小(可借助无符号类型完成)。例如:
    char* someCopy( char *str) {
    char result[20];
    int len = strlen(str);
    if(len <20) {
    ...
    }
    ...
    }
    注意strlen的返回类型为size_t,这通常是一个unsigned long类型,如果传入字符串长度超过了INT_MAX,如当长度为INT_MAX+1时,根据A中的规则可知,这时len会变成一个负数,从而使接下来的len<20始终为真,攻击者如果传入一个2G的数据,则可使其发生溢出。
  • 相关阅读:
    设计阶段如何画用例视图(UseCase View)
    如何进行博客中的摘要设计
    浅析Microsoft .net PetShop程序中的购物车和订单处理模块(Profile技术,异步MSMQ消息)<转>
    设计漂亮的样式表是一门艺术<轉>
    推荐几个Net开源项目
    使用Ajax和Jquery实现GridView的展开、合并
    36个引人注目JQuery导航菜单
    Nunit中如何进行事务性单元测试
    推荐Jquery 40个漂亮的导航菜单设计
    Highcharts:高交互性的javascript图表类库
  • 原文地址:https://www.cnblogs.com/qianyuming/p/2443429.html
Copyright © 2011-2022 走看看