SGCheck:一个实验堆栈和全局数组溢出检测器

来源:互联网 发布:ed2k下载工具 mac 编辑:程序博客网 时间:2024/06/09 02:37

目录

11.1。概观
11.2。SGCheck命令行选项
11.3。SGCheck如何工作
11.4。与Memcheck进行比较
11.5。限制
11.6。仍然要做:用户可见的功能
11.7。仍然要做:实施整顿

要使用此工具,必须--tool=exp-sgcheck在Valgrind命令行上指定 。

11.1。概观

SGCheck是一种查找堆栈和全局数组超量的工具。它通过使用从关于堆栈和全局数组访问的可能形式的观察得出的启发式方法来工作。

11.2。SGCheck命令行选项

目前没有SGCheck特定的命令行选项。

11.3。SGCheck如何工作

当源文件编译时-g,编译器将附加DWARF3调试信息,该信息描述文件中所有堆栈和全局数组的位置。

如果编译器也可以告诉我们每个内存引用指令应该访问哪个数组(如果有的话),则对这样的数组的访问的检查将是相对简单的。不幸的是,DWARF3调试格式不能提供一种表示这些信息的方法,所以我们必须采用启发式技术来近似它。关键的观察结果是, 如果存储器引用指令访问堆栈或全局数组一次,则很可能总是访问相同的数组

要看看这可能有用,请考虑以下buggy片段:

   {int i,a [10]; //都是自动变量     for(i = 0; i <= 10; i ++)        a [i] = 42;   }

在运行时,我们将知道a[]堆栈的精确地址,因此我们可以观察到由a[i] = 42写入产生的第一个存储a[],并且我们将(正确地)假设该指令始终被访问a[]。然后,在第11次迭代中,它访问其他地方,可能是不同的本地,可能是未占用堆栈的区域(例如,溢出槽),因此SGCheck报告错误。

有一个重要的警告。

想象一下这样的功能,memcpy用于在程序的整个生命周期中读取和写入许多不同的内存区域。如果我们坚持在其内存复制循环中的读写指令只访问一个特定的堆栈或全局变量,那么我们将被调用导致错误 memcpy

为了避免这个问题,SGCheck会为函数的每个条目实例化新的可能的目标记录,并在退出时将其丢弃。这允许检测(例如)memcpy 对于任何特定呼叫的源或目的地缓冲器溢出的情况,但是不会从一个呼叫到下一个呼叫的任何限制。实际上,多线程可以进行多个同时调用(例如)memcpy而不会相互干扰。

需要注意的是该协会之间进行是很重要的二进制指令和阵列中, 第一次这个二进制指令访问一个函数调用中的数组。当在相同的函数调用期间再次执行相同的指令时,如果这些进一步的执行没有访问相同的数组,则SGCheck可能会报告问题。该技术在SGCheck中引起了一些限制,请参阅 限制。

11.4。与Memcheck进行比较

SGCheck和Memcheck是互补的:它们的功能不重叠。Memcheck对堆数组执行边界检查和免用后检查。它还可以使用由堆或堆栈分配创建的未初始化值。但是它不会对堆栈或全局数组执行边界检查。

另一方面,SGCheck确实会检查堆栈或全局数组,但是它不做任何其他操作。

11.5。限制

这是一个实验工具,它依赖于对正确程序行为的一些不那么强硬的假设。你应该注意到一些限制。

  • 虚假的否定(错误的错误):从上面的描述(SGCheck Works)可以看出,存储器引用指令到堆栈或全局数组的第一次访问会创建该指令与数组之间的关联,后者将在后续访问中检查该指令,直到包含函数退出。因此,由于SGCheck将其用作后续访问应该行为的“示例”,所以不会检查对数组(在任何给定的函数实例化中)的指令的首次访问。

    这也意味着在只执行一次的指令中将不会发现错误(例如,因为该指令不在循环中,或循环仅执行一次)。

  • 虚假的错误(错误的错误):同样,更严重的是,很可能写出合法的代码片断,破坏了检查算法所依赖的基本假设。例如:

      {int a [10],b [10],* p,i;    for(i = 0; i <10; i ++){       p = / *任意条件* /?&a [i]:&b [i];       * p = 42;    }  }

    在这种情况下,商店有时会访问a[],有时会访问b[],但是在任何情况下,寻址的阵列都将超载。然而,目标的变化将导致报告错误。

    很难看出如何解决这个问题。唯一的缓解因素是,这样的结构看起来非常罕见,至少从使用该工具的结果到目前为止。这样的一个建筑在Valgrind的源头(在Valgrind运行Valgrind)只会出现一次,也许在Firefox的启动和退出两三次。可以做的最好的是抑制错误。

  • 性能:SGCheck必须读取可执行文件及其共享对象上的所有DWARF3类型和变量信息。这在计算上是昂贵的,使得启动相当缓慢。对于OpenOffice大小的应用程序,在2.4 GHz Core 2机器上,您可以期待debuginfo读取时间在一分钟的时间内。读这个信息也需要很多的记忆。为了使其可行,SGCheck在压缩DWARF3数据的内存中的表示方面遇到了相当大的麻烦,这就是读取过程看起来慢的原因。

  • 性能:SGCheck运行速度比Memcheck慢。这部分是由于缺乏调整,但部分是由于算法困难。堆栈和全局检查有时可能需要对每个存储器访问进行多个范围检查,并且尽管作出了相当大的努力,但这些难以短路。重新设计和重新实现可能会使其更快。

  • 覆盖:堆栈和全局检查是脆弱的。如果共享对象没有附加调试信息,则SGCheck将无法确定该共享对象中定义的任何堆栈或全局数组的边界,因此无法检查对它们的访问。即使从使用调试信息编译的其他共享对象访问这些数组时,也是如此。

    目前,SGCheck接受缺少debuginfo的对象,无需注释。这是危险的,因为它会导致SGCheck静默地跳过堆栈和全局检查这些对象。最好在这种情况下打印警告。

  • 覆盖范围:SGCheck不检查系统调用读或写的区域是否覆盖堆栈或全局数组。这很容易添加。

  • 平台:堆栈/全局检查在PowerPC,ARM或S390X平台上无法正常工作,仅在X86和AMD64目标上。这是因为堆栈和全局检查需要跟踪函数调用并可靠地退出,并且在使用链接寄存器进行函数返回的ABI上没有明显的方法。

  • 鲁棒性:与前一点相关。X86和AMD64的函数调用/退出跟踪被认为即使在同一个堆栈中存在longjmps也能正常工作(尽管尚未测试)。然而,切换堆栈的代码可能会导致破坏/混乱。

11.6。仍然要做:用户可见的功能

  • 扩展系统调用检查以在堆栈和全局数组上工作。

  • 如果共享对象没有附加调试信息,或者由于某种原因无法找到或读取调试信息,则打印警告。

  • 添加一些启发式过滤,消除明显的误报。这很容易做到。例如,从堆到堆栈对象的访问几乎肯定不是一个错误,所以不应该向用户报告。

11.7。仍然要做:实施整顿

标记为“临界”的项目对于正确性被认为是重要的:不确定它们可能导致实际使用中的崩溃或断言失败。

  • sg_main.c:重新设计并重新实现基本检查算法。它可以比它快得多 - 目前的实现不是很好。

  • sg_main.c:通过执行一些前期过滤来提高堆栈/全局检查的性能,以忽略“显然”不能是堆栈或全局变量的区域中的引用。这将需要使用m_aspacemgr知道地址空间布局的信息。

  • sg_main.c:修复compute_II_hash,使ppc32 / 64目标更加明智(除了sg_不适用于ppc32 / 64目标,所以这在目前有点学术)。


原创粉丝点击