C++数值运算与类型安全

在理想情况下,对类型的错误应用会导致一些错误,并让我们第一时间发觉;在最糟的情况下,其错误在很久之后才被发现,而且那时我们的系统已经遭受了足够多的攻击。

C/C++几个基本的类型规则:

类型转换规则:

系统将精度高低规定如箭头所示。 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位值的上限。

强制类型转换:

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

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++语法是这样),最终条件判断通过,有兴趣的可以试一试。

运算

在数值运算中,主要考虑其二进制变化即可。

加减运算

N位无符号数从0-2^N-1变化,N位有符号数从 -2^(N-1) - 2^(N-1)-1,如果加减运算超过上限或下限,则可以将数值范围视为一个环来推算。

乘法运算

 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的数据,则可使其发生溢出。