访问数组越界有多危险?

Chr*_*isD 219 c memory arrays

访问其边界之外的数组有多危险(在C中)?有时候我会从数组外部读取(我现在理解我然后访问我的程序的某些其他部分使用的内存,甚至超过它)或者我试图将值设置为数组之外的索引.该程序有时会崩溃,但有时只是运行,只会产生意想不到的结果.

现在我想知道的是,这真的有多危险?如果它损坏我的程序,那就不是那么糟糕了.另一方面,如果它打破了我的程序之外的东西,因为我以某种方式设法访问一些完全不相关的内存,那么它是非常糟糕的,我想.我读了很多"任何可能发生的事情","分割可能是最不好的问题","你的硬盘可能会变成粉红色,独角兽可能会在你的窗下唱歌",这很好,但真正的危险是什么?

我的问题:

  1. 从阵列外部读取值会损坏我的程序之外的任何东西吗?我想象只是看事情不会改变任何东西,或者它会改变我碰巧触及的文件的'last time opened'属性?
  2. 除了我的程序之外,在数组外部设置值可以破坏任何东西吗?从这个 Stack Overflow问题我收集到可以访问任何内存位置,没有安全保证.
  3. 我现在从XCode中运行我的小程序.这是否为我的程序提供了一些额外的保护,它无法到达自己的内存之外?它会伤害XCode吗?
  4. 有关如何安全地运行我的固有错误代码的任何建议?

我使用OSX 10.7,Xcode 4.6.

Kei*_*son 120

就ISO C标准(语言的官方定义)而言,访问其边界之外的数组具有" 未定义的行为 ".字面意思是:

使用不可移植或错误的程序结构或错误数据时的行为,本国际标准不对此要求

非规范性说明扩展了这一点:

可能的未定义行为包括完全忽略具有不可预测结果的情况,在转换或程序执行期间以环境特征(有或没有发出诊断消息)的特定文档执行,终止翻译或执行(发布时)一条诊断信息).

这就是理论.现实是什么?

在"最佳"的情况下,你会获得一些片的那要么你当前运行的程序(这可能会导致程序无法正常运作)拥有的内存,或者某个被你的当前运行程序所拥有(这可能会导致你的程序像分段错误一样崩溃).或者您可能尝试写入程序拥有的内存,但标记为只读; 这可能也会导致程序崩溃.

这假设您的程序在一个操作系统下运行,该操作系统试图保护彼此同时运行的进程.如果您的代码在"裸机"上运行,比如它是OS内核或嵌入式系统的一部分,那么就没有这样的保护; 你行为不端的代码应该提供保护.在这种情况下,损坏的可能性要大得多,包括在某些情况下对硬件(或附近的物品或人)的物理损坏.

即使在受保护的操作系统环境中,保护也不总是100%.例如,存在允许非特权程序获得根(管理)访问的操作系统错误.即使具有普通用户权限,故障程序也会消耗过多的资源(CPU,内存,磁盘),可能会导致整个系统崩溃.许多恶意软件(病毒等)利用缓冲区溢出来获得对系统的未授权访问.

(一个历史的例子:我听说在一些带有核心内存的旧系统中,在紧密循环中重复访问单个内存位置可能会导致该内存块融化.其他可能性包括破坏CRT显示和移动读取/写入驱动器柜谐波频率的磁盘驱动器磁头,使其在桌子上走动并落到地板上.)

天网总是担心.

最重要的是:如果你可以写一个程序来做一些故意的事情,至少在理论上可能有一个错误的程序可能会意外地做同样的事情.

在实践中,这是非常不可能的MacOS X的系统上运行你的错误的程序会做什么比暴跌更加严重.但是,不可能完全阻止错误代码执行非常糟糕的事情.

  • @ChrisD:我们往往很幸运.8-)}说真的,OS级别的保护现在相当不错.最糟糕的情况是,如果我写一个意外的[fork bomb](http://en.wikipedia.org/wiki/Fork_bomb),我可能需要重新启动才能恢复.但是对系统的真正损害可能并不值得担心,只要你的程序没有试图在危险的边缘做某事.如果您真的担心,在虚拟机上运行程序可能不是一个坏主意. (6认同)
  • 另一方面,我在我使用过的计算机上看到过很多奇怪的事情(损坏的文件、不可恢复的系统错误等),我不知道其中有多少可能是由某些 C 程序引起的可怕的未定义行为。(到目前为止,还没有真正的恶魔从我的鼻子里飞出来。) (2认同)
  • http://www.scientificamerican.com/article/printers-can-be-hacked-to-catch-fire/因此现代电子设备仍可能发生火灾。 (2认同)

tru*_*cks 25

通常,今天的操作系统(无论如何都是流行的)使用虚拟内存管理器在受保护的内存区域中运行所有应用程序.事实证明,简单地读取或写入已经分配/分配给您的进程的区域之外的REAL空间中的位置并不是非常容易(根据说).

直接答案:

1)读取几乎不会直接损坏另一个进程,但如果您碰巧读取用于加密,解密或验证程序/进程的KEY值,则它可能间接地损坏进程.如果您根据正在阅读的数据做出决策,那么读取界限可能会对您的代码产生一些不利/意外的影响

2)通过写入内存地址可访问的内容来实现损坏的唯一方法是,您写入的内存地址实际上是硬件寄存器(实际上不是用于数据存储而是用于控制某个部分的位置)硬件)不是RAM位置.事实上,除非你正在写一些不可重写的可编程位置(或那种性质的东西),否则你通常不会损坏某些东西.

3)通常从调试器内运行在调试模式下运行代码.在调试模式下运行TEND(但并不总是)在您完成某些未经考虑的操作或非法的非法操作时更快地停止代码.

4)永远不要使用宏,使用已经内置数组索引边界检查的数据结构等....

另外 我应该补充一点,上述信息实际上仅适用于使用带内存保护窗口的操作系统的系统.如果为嵌入式系统甚至是使用没有内存保护窗口(或虚拟寻址窗口)的操作系统(实时或其他)的系统编写代码,那么在读取和写入内存时应该更加谨慎.同样在这些情况下,应始终采用SAFE和SECURE编码实践来避免安全问题.

  • 应始终**使用安全可靠的编码实践. (4认同)
  • 我建议不要使用try/catch来获取错误代码,除非你捕获非常具体的异常并知道如何从中恢复.Catch(...)是你可以添加到有缺陷的代码最糟糕的事情. (3认同)

che*_*che 9

不检查边界可能导致丑陋的副作用,包括安全漏洞.其中一个丑陋的是任意代码执行.在经典示例中:如果您有一个固定大小的数组,并且用于strcpy()在其中放置用户提供的字符串,则用户可以为您提供一个溢出缓冲区的字符串并覆盖其他内存位置,包括CPU应在函数返回的代码地址饰面.

这意味着您的用户可以向您发送一个字符串,该字符串将使您的程序基本上调用exec("/bin/sh"),这会将其转换为shell,在系统上执行他想要的任何操作,包括收集所有数据并将您的计算机转换为僵尸网络节点.

有关如何完成此操作的详细信息,请参阅Smashing The Stack for Fun和Profit.


Udo*_*ein 8

你写:

我读了很多"任何可能发生的事情","细分可能是最不好的问题","你的硬盘可能变成粉红色,独角兽可能会在你的窗下唱歌",这很好,但真正的危险是什么?

让我们这样说:装枪.将它指向窗外,没有任何特定的目标和火.有什么危险?

问题是你不知道.如果你的代码覆盖了崩溃你的程序的东西你就可以了,因为它会将它停止到一个已定义的状态.但是,如果它没有崩溃,那么问题就会开始出现.哪些资源可以控制您的程序以及它可以对它们做些什么?哪些资源可能会受到您的程序的控制,以及它可能对它们有什么影响?我知道至少有一个由这种溢出引起的重大问题.这个问题出现在一个看似毫无意义的统计函数中,这个函数搞砸了生产数据库的一些不相关的转换表.结果是之后进行了一些非常昂贵的清理工作.实际上,如果这个问题已经格式化硬盘,它会更便宜,更容易处理......换句话说:粉红色的独角兽可能是你最不容易的问题.

您的操作系统将保护您的想法是乐观的.如果可能的话尽量避免写出界限.

  • 谁说过任何事情都是安全的;) (3认同)

mik*_*yra 7

不以root身份运行程序或任何其他特权用户不会损害您的任何系统,因此通常这可能是一个好主意.

通过将数据写入某个随机存储器位置,您不会直接"损坏"计算机上运行的任何其他程序,因为每个进程都在其自己的存储空间中运行.

如果您尝试访问未分配给您的进程的任何内存,操作系统将阻止您的程序执行分段错误.

因此直接(不以root身份运行并直接访问/ dev/mem等文件),程序不会干扰操作系统上运行的任何其他程序.

尽管如此 - 可能这就是你在危险方面所听到的 - 通过盲目地将随机数据随意写入随机存储位置你肯定会损坏任何你能够损坏的东西.

例如,您的程序可能希望删除存储在程序中某处的文件名给出的特定文件.如果偶然您只是覆盖存储文件名的位置,您可能会删除一个非常不同的文件.


jbg*_*bgs 1

除了你自己的程序之外,我认为你不会破坏任何东西,在最坏的情况下,你会尝试从与内核未分配给你的进程的页面相对应的内存地址读取或写入,从而生成正确的异常并被杀死(我的意思是,你的过程)。

  • ..什么?在您自己的进程中覆盖用于存储稍后使用的某些变量的内存怎么样?现在它的值已经神秘地改变了!我向你保证,追踪这些错误是非常有趣的。段错误将是“最好的”结果。-1 (3认同)
  • 多年前,我曾经是一个笨拙的 C 程序员。我访问超出其范围的数组数百次。除了我的进程被操作系统杀死之外,什么也没发生。 (3认同)
  • 我的意思是,除了他自己的程序之外,他不会“破坏”其他进程;) (2认同)