根据维基百科(http://en.wikipedia.org/wiki/Buffer_overflow)
通常与缓冲区溢出相关的编程语言包括C和C ++,它们不提供内置保护以防止访问或覆盖内存的任何部分中的数据,也不会自动检查写入数组的数据(内置缓冲区类型) )在该数组的边界内。边界检查可以防止缓冲区溢出。
那么,为什么'Bounds Checking'没有在某些语言中实现,比如C和C ++?
答案 0 :(得分:10)
基本上,这是因为它意味着每次更改索引时,都必须执行if语句。
让我们考虑一个简单的C for循环:
int ary[X] = {...}; // Purposefully leaving size and initializer unknown
for(int ix=0; ix< 23; ix++){
printf("ary[%d]=%d\n", ix, ary[ix]);
}
如果我们有边界检查,生成的ary[ix]
代码必须类似于
LOOP:
INC IX ; add `1 to ix
CMP IX, 23 ; while test
CMP IX, X ; compare IX and X
JGE ERROR ; if IX >= X jump to ERROR
LD R1, IX ; put the value of IX into register 1
LD R2, ARY+IX ; put the array value in R2
LA R3, Str42 ; STR42 is the format string
JSR PRINTF ; now we call the printf routine
J LOOP ; go back to the top of the loop
;;; somewhere else in the code
ERROR:
HCF ; halt and catch fire
如果我们没有那个边界检查,那么我们可以改为编写:
LD R1, IX
LOOP:
CMP IX, 23
JGE END
LD R2, ARY+R1
JSR PRINTF
INC R1
J LOOP
这样可以在循环中保存3-4条指令,(特别是在过去)这意味着很多。
事实上,在PDP-11机器中,它甚至更好,因为有一种称为“自动增量寻址”的东西。在PDP上,所有寄存器等都变成了类似
的东西CZ -(IX), END ; compare IX to zero, then decrement; jump to END if zero
(任何碰巧比我更好地记住PDP的人,不要给我带来精确语法等方面的麻烦;你是一个像我这样的老屁,你知道这些东西是如何溜走的。)
答案 1 :(得分:3)
在编译和运行时更容易实现和更快。它还简化了语言定义(如果跳过这一点,可以省略很多事情。)
目前,当你这样做时:
int p =(int )malloc(sizeof(int)); * p = 50;
C(和C ++)只是说,“Okey dokey!我会在记忆中放置一些东西”。
如果需要边界检查,C必须说:“好的,首先让我看看我能不能把东西放在那里?它已被分配了吗?是的?很好。我现在就插入。”通过跳过测试来查看是否有可以在那里写的东西,你节省了一个非常昂贵的步骤。另一方面,(她戴着手套),我们现在生活在一个“优化适合那些买不起RAM的人”的时代,所以关于速度的争论变得越来越弱。
答案 2 :(得分:3)
这完全取决于性能。但是,C和C ++没有边界检查的断言并不完全正确。拥有每个库的“调试”和“优化”版本是很常见的,并且在各种库的调试版本中找到启用边界检查的情况并不少见。
这样做的好处是可以在开发应用程序时快速轻松地找到越界错误,同时在运行realz程序时消除性能损失。
我还应该补充说,性能损失是不可忽略的,并且C ++以外的许多语言将提供在缓冲区上运行的各种高级函数,这些函数直接在C和C ++中实现,以避免边界检查。例如,在Java中,如果比较使用纯Java与使用System.arrayCopy复制一个数组的速度(使用System.arrayCopy进行边界检查,然后直接复制数组而不对每个元素进行边界检查),你会发现这两个操作的性能差异很大。
答案 3 :(得分:1)
主要原因是为C或C ++添加边界检查的性能开销。虽然这种开销可以通过最先进的技术大幅降低(20-100%的开销,取决于应用程序),但它仍然足以让许多人犹豫不决。我不确定这种反应是否合理 - 我有时会怀疑人们过分关注绩效,仅仅因为绩效是可量化的和可衡量的 - 但无论如何,这都是生活中的事实。这一事实减少了主要编译器努力将最新的边界检查工作集成到编译器中的动机。
次要原因是担心边界检查可能会破坏您的应用。特别是如果您使用指针算法和强制转换来破坏标准,那么边界检查可能会阻止您的应用程序正在执行的操作。大型应用程序有时会做出令人惊讶的狡猾和丑陋的事情。如果编译器破坏了应用程序,那么指责问题的狡猾代码就没有意义了;人们不会继续使用破坏其应用程序的编译器。
另一个主要原因是边界检查与ASLR + DEP竞争。 ASLR + DEP被认为是解决问题,哦,80%的问题左右。这减少了对完整边界检查的感知需求。
答案 4 :(得分:1)
因为它会削弱那些用于HPC要求的通用语言。有很多应用程序,缓冲区溢出与iota无关,只是因为它们没有发生。这样的功能在库中要好得多(实际上你已经可以找到C / C ++的例子了)。 对于特定领域的语言,可以将这些特性烘焙到语言定义中并交换产生的性能,以提高安全性。