Programming languages 为什么在某些语言中没有实现边界检查?

Programming languages 为什么在某些语言中没有实现边界检查?,programming-languages,implementation,buffer-overflow,Programming Languages,Implementation,Buffer Overflow,根据维基百科(http://en.wikipedia.org/wiki/Buffer_overflow) 与缓冲区溢出相关联的编程语言包括C和C++,它们不提供内置的保护,以防止访问或覆盖内存的任何部分中的数据,并且不会自动检查写入数组(内置缓冲类型)的数据是否在该数组的边界内。边界检查可以防止缓冲区溢出 P>那么,为什么在C语言和C++语言中没有实现“边界检查”? ,编译和运行时都比较容易实现,而且更快。它还简化了语言定义(如果跳过它,可以省略很多内容) 当前,当您执行以下操作时: int

根据维基百科(http://en.wikipedia.org/wiki/Buffer_overflow)

与缓冲区溢出相关联的编程语言包括C和C++,它们不提供内置的保护,以防止访问或覆盖内存的任何部分中的数据,并且不会自动检查写入数组(内置缓冲类型)的数据是否在该数组的边界内。边界检查可以防止缓冲区溢出


<> P>那么,为什么在C语言和C++语言中没有实现“边界检查”?

,编译和运行时都比较容易实现,而且更快。它还简化了语言定义(如果跳过它,可以省略很多内容)

当前,当您执行以下操作时:

int p=(int)malloc(sizeof(int)); *p=50

C(和C++)只是说,“好的,我会把一些东西放在内存中的那个位置。”


如果需要边界检查,C将不得不说,“好的,首先让我们看看我是否可以将某些内容放在那里?是否已分配?是的?很好。我现在插入。”通过跳过测试以查看是否可以在那里写入某些内容,您节省了一个非常昂贵的步骤。另一方面,(她戴着手套),我们现在生活在一个“优化是那些买不起RAM的人”的时代,所以关于速度的争论越来越弱。

主要原因是向C或C++添加边界检查的性能开销。虽然使用最先进的技术可以大幅降低这一开销(根据应用程序的不同,可以降低到20-100%的开销),但它仍然很大,足以让许多人犹豫不决。我不确定这种反应是否合理——我有时怀疑人们过于关注绩效,仅仅因为绩效是可量化和可测量的——但不管怎样,这是生活中的一个事实。这一事实降低了主要编译器将最新的边界检查工作集成到编译器中的动机

第二个原因是担心边界检查可能会破坏你的应用程序。特别是如果你在指针运算和强制转换方面做了一些违反标准的事情,那么边界检查可能会阻止你的应用程序当前正在做的事情。大型应用程序有时会做一些非常粗糙和丑陋的事情。如果编译器破坏了应用程序,那么就没有必要把问题归咎于粗糙的代码;人们不会继续使用破坏应用程序的编译器


另一个主要原因是边界检查与+。ASLR+DEP被认为解决了80%左右的问题。这减少了对全面边界检查的需求。

基本上,这是因为这意味着每次更改索引时,都必须执行if语句

让我们考虑循环的一个简单的C:< /P>

int ary[X] = {...};  // Purposefully leaving size and initializer unknown

for(int ix=0; ix< 23; ix++){
    printf("ary[%d]=%d\n", ix, ary[ix]);
}
如果我们没有边界检查,那么我们可以写:

    LD R1, IX
LOOP:
    CMP IX, 23
    JGE END
    LD R2, ARY+R1
    JSR PRINTF
    INC R1
    J   LOOP
这在循环中节省了3-4条指令,这(特别是在过去)意义重大

事实上,在PDP-11机器中,它甚至更好,因为有一种叫做“自动增量寻址”的东西。在PDP上,所有的寄存器等都变成了

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
CZ  -(IX), END    ; compare IX to zero, then decrement; jump to END if zero

(任何比我更记得PDP的人,请不要给我关于精确语法等的麻烦;你和我一样是个老屁,你知道这些东西是如何溜走的。)

这都是关于性能的。但是,C和C++没有边界检查的断言并不完全正确。每个库都有“调试”和“优化”版本是很常见的,在各种库的调试版本中启用边界检查也很常见

这样做的优点是,在开发应用程序时可以快速、轻松地发现越界错误,同时在运行realz程序时消除性能影响


<>我也应该补充,性能命中是不可忽略的,除了C++之外,许多语言都会提供各种在缓冲区中运行的高级函数,这些函数直接在C和C++中实现,以避免边界检查。例如,在Java中,如果您比较使用纯Java将一个数组复制到另一个数组的速度与使用System.arrayCopy(System.arrayCopy只执行一次边界检查,但随后直接复制数组而不检查每个元素的边界)的速度,您将看到这两种操作的性能有相当大的差异。

因为这将削弱那些通用语言的HPC需求。有很多应用程序缓冲区溢出实际上一点也不重要,因为它们不会发生。这样的特性在库中更好(事实上,您已经可以在库中找到C/C++的示例)。
对于特定于领域的语言,将这些功能嵌入语言定义中并用由此产生的性能损失换取更高的安全性可能是有意义的。

这是一种开销,并非总是需要的。一些程序员认为这是不需要的开销。“那些永远不会犯错误的。”Hans,这是开发应用程序和运行单元测试时所必需的;但是,当它实际运行时,它是没有帮助的,因为可能代码已经实现了边界检查,作为访问它的逻辑的一部分。这符合单元测试人员的类别,他们认为他们从来没有犯过错误。他们应该会面并思考为什么程序仍然存在bug。也许可以得出结论,这是用户的错。这是一些应用程序确实不需要的开销。指责程序员的无知/傲慢是天真的。当然。见鬼,这是用Algol-60完成的。那时候也是一种开销。当Kernighan、Ritchie、Thompson等人构建C和UNIX时,他们选择不这样做是为了将所有这些指令保存起来用于他们自己的邪恶目的。当吉姆·戈斯林和橡树人建造proto Java时,他们