CMBC未报告看似无效的内存访问

时间:2016-03-31 09:35:52

标签: c pointers undefined-behavior cbmc

我有以下代码段。

#include<stdio.h>
#include<stdlib.h>
int main()
{
        char *c = malloc(1);
        printf("%p\n", c);
        c = c + 20;

        printf("%p\n", c);
        printf("%d\n", *c);

        free(c - 20);
        return 0;
}

在这段代码中,我将1个字节的内存分配给指针。然后我访问分配内存后20个单元的内存位置。当我取消引用该指针时,我预计会出现内存访问冲突错误或分段错误或类似的内容。我没有收到任何此类错误。

让我们假设这是未定义行为的情况。所以我尝试使用CBMC验证这个程序,这是一个众所周知的模型检查器,使用以下命令。

cbmc test01.c --pointer-check

CBMC报道该计划是安全的。这是CBMC的问题还是我错过了什么?

2 个答案:

答案 0 :(得分:1)

正如您在问题中所说,语句printf("%d\n", *c);暴露了未定义的行为。由于未定义,您对此可能产生的任何期望都是错误的。这包括获取特定错误或任何错误。

C运行时库不检查程序访问内存的方式。如果它会这样做,程序会运行得多,慢得多。关于堆,C运行时库执行一些基本检查;例如,它在程序启动期间在地址0处放置一定值,并在程序结束时检查该值是否仍然存在。如果值已更改,则空指针将被取消引用以进行写入,并且它可以向您发出警告。

c = c + 20;之后,c最有可能指向属于您的程序的内存块。它可以是堆上的空闲区域,它可以在堆管理器用来处理堆的数据结构内部,但很可能它仍然在同一个内存页面上。

如果您有(坏)运气和c + 20落在存储c的内存页面之外,则会发生由操作系统处理的异常情况。它终止程序并显示与您在问题中列出的错误消息类似的错误消息(想法是相同的,每个操作系统上的单词和表示都不同)。

<强>更新

分配内存不是某种魔力。该程序以一块内存(称为&#34; heap&#34; )开始,由操作系统为此目的分配给程序。

C运行时库包含管理堆的代码。此代码使用此内存的一小部分进行簿记。一个常见的实现使用双链表,列表中每个节点的有效负载是一块内存&#34;已分配&#34;由程序使用<memory.h>malloc()calloc()等中声明的函数。当调用malloc()时,此代码运行,在列表中创建一个新节点,并返回节点有效负载的地址(堆内的地址)。

程序根据需要使用此指针。例如,您的程序可以在c-1免费撰写。事实上,在malloc()里面,它实际上在那里写了信息。在malloc()返回c后,您的代码也可以在c-1处撰写。从OS的角度来看,这两个写操作之间没有区别。并且由于C不是托管或解释语言,因此程序中不包含任何代码,用于查看您编写的代码所执行的代码,或者保留其手写不在错误的位置。

如果您在c-1写信,那么您很可能会破坏堆管理器使用的数据结构。没有错误立即发生。没有显示错误消息,您的程序继续运行显然正常。但是在下一次调用处理堆的函数(无论是内存分配还是释放)时,程序将开始造成严重破坏。堆数据结构损坏可能发生任何事情。

关于CBMC,我不知道它是如何运作的。也许它无法检测到这种情况。或者它可能会报告您的程序是安全的,因为它在c增加后不会写入。

答案 1 :(得分:0)

顺便说一句。 gcc -fsanitize=address会在没有任何警告的情况下编译此文件,但是当您运行代码时,您将收到来自地址sanitzer的消息

=================================================================
==24198==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000f004 at pc 0x400921 bp 0x7ffe1e66b900 sp 0x7ffe1e66b8f0
READ of size 1 at 0x60200000f004 thread T0
    #0 0x400920 in main (/home/ingo/test/c/sanitize_address+0x400920)
    #1 0x7fdd9ddca7af in __libc_start_main (/lib64/libc.so.6+0x207af)
    #2 0x4007d8 in _start (/home/ingo/test/c/sanitize_address+0x4007d8)

0x60200000f004 is located 19 bytes to the right of 1-byte region [0x60200000eff0,0x60200000eff1)
allocated by thread T0 here:
    #0 0x7fdd9e19c7b7 in malloc (/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/libasan.so.1+0x577b7)
    #1 0x4008b7 in main (/home/ingo/test/c/sanitize_address+0x4008b7)
    #2 0x7fdd9ddca7af in __libc_start_main (/lib64/libc.so.6+0x207af)

SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 main
Shadow bytes around the buggy address:
  0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa 01 fa
=>0x0c047fff9e00:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Contiguous container OOB:fc
  ASan internal:           fe
==24198==ABORTING

这样的输出可以非常有助于发现这种泄漏和超支。

但编译器找到运行时发生的错误并不是一项简单的任务。