原文: http://avikivity.blogspot.com/2011/08/c-assembly-and-security.html
ykyi.net 翻译。
我们看下面一个C语言的句子:
a = b + c
什么时候这个句子会造成错误呢?
1. a, b, c 不是我们想要的值。也就是我们把变量名写错了。
2. 写了加法,但是我们要的不是加法。
3. 做了加法后值溢出了。
4. a和b是无符号类型,而c却是有符号类型并且是负数,b+c后得到负数又赋给a.
5. sizeof(a)比sizeof(b)和sizeof(c)要小。又溢出了!
6. a是有符号型,b和c是无符号型。b+c后赋给a,溢出后,a变成负值。
7. a是无符号型,b和c是有符号型;再次溢出。
我们不能期望C语言本身能避免所有这些错误,但我们可不可以通过在运行时Trap上述的一些错误改进C语言使之更安全呢?结果时,除非牺牲性能,否则做不到。但我们能牺牲性能吗?
♦ 为了处理第(3)种情况,我们需要Trap所以有符号数的加法和所以无符号数的加法指令。
♦ 为了处理第(4)种情况,需要一个混杂有符号和无符号的加法指令被Trap。
♦ 为了处理第(5)种情况,需要Trap存储有符号数和无符号数的指令。在Trap中检查寄存器中的值与存入的内存地址是否匹配。
♦ (6)和(7)与上类似。
这些看似简单的问题在安全,漏洞方面经常出现。我们很难发现并修复这些问题,因为没有所需要的处理器指令。当然我们可以用已经存在的指令模拟它们。但是这样做肯定会损害性能并且使程序的体积增大。因为程序性能是可以被度量的,而度量安全问题则比较困难。结果时留下了很多充满漏洞的代码(exploitable code)。
不禁要问,为什么我们没有这些指令呢?在上世纪的七,八十年代,正在快速成长的计算机工业界面临的最大问题是性能,而不是安全。那时的代码量很小,也很容易做检查;网络还在最初级的阶段,很小,而且基本上是私有的;基本不存在恶意的攻击。
C语言在当时的作法是为了能够在适合当时所有的处理器,因此C语言的语义模仿当时的处理器指令集。之后,C语言大获成功,大量的处理器则针对C语言做优化,很多指令直接针对C语言的语义做优化或可以直接对应到C语言的语义。这样,使得C语言更加流行了。
X86的世界里有两个指令: INTO和BOUND是极好的例子。INTO(INTerrupt on Overflow)可以在一个加法指令或者减法指令之后。INTO可以用来作为上文指到过的Trap有符号加减法的指令。BOUND则对数组操作是否越界做检查。但是BOUND极少被使用。最后,在x86体系过渡到64位体系的过程中,INTO指令和BOUND指令都被只留在历史的尘埃之中了。
copyright ykyi.net