gcc 地址清理程序不检测越界

tzcvj98z  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(195)

我正在学习地址清理程序:

#include <stdio.h>
int main(int argc, char **argv)
{
    int array[30]={0};
    (void) printf("%d\n", array[179]); // <--- should crash, right?    
    return 0;
}

但我无法触发任何错误/警告:

$ gcc -c main.c -fsanitize=address -o main.o
$ gcc main.o -fsanitize=address -o main
$ ./main
32765
xesrikrc

xesrikrc1#

-fsanitize=address的情况下,使用有限大小的影子存储器检查存储器访问,其中堆栈缓冲区由两个红色区域包围(一个在缓冲区之前,一个在缓冲区之后)。如果访问发生在红色区域内,Asan会检测到错误并中止执行。但是,这些红色区域非常小,因为它们是设计用于检测缓冲区溢出/溢出错误的。而不是具有大索引的野生越界访问。
你的情况是这样的:

Shadow bytes: 00 f1 f1 f1 f1 00 00 00 00 00 f3 f3 f3 f3 00 00 ... [00]
                |  redzone  |     array    |  redzone  |           ^^
                                                            read happens here

Addressable:             00
Stack left redzone:      f1
Stack right redzone:     f3

而且错误的内存读取也不会被检测到。
您要检测的此类错误是-fsanitize=undefined,或者更具体地说是-fsanitize=bounds-fsanitize=bounds-strict之一。在这种情况下,边界检查将考虑数组的定义,并针对通过其标识符进行的每次访问检查其类型和边界。

$ gcc -fsanitize=undefined main.c -o main
$ ./main
main.c:5:20: runtime error: index 179 out of bounds for type 'int [30]'
main.c:5:20: runtime error: load of address 0xfffffffff68c with insufficient space for an object of type 'int'
0xfffffffff68c: note: pointer points here
  87 08 00 00 00 00 00 00  06 00 00 00 00 00 00 00  00 10 00 00 00 00 00 00  11 00 00 00 00 00 00 00
              ^
0

但请注意,这通常不会保存您执行以下操作:

static void foo(int *arr) {
    printf("%d\n", arr[179]);
}

int main(void) {
    int array[30] = {0};
    foo(array);
    return 0;
}

因为在这种情况下,arr的边界在函数foo中是未知的。
或者,您也可以考虑manually poisoning阴影内存:

#include <stdio.h>
#include <sanitizer/asan_interface.h>

int main(void) {
    int array[30] = {0};

    // Poison 200 * sizeof(int) bytes after the end of array
    ASAN_POISON_MEMORY_REGION(arr + 30, 200 * sizeof(int));

    printf("%d\n", arr[179]); // Now this will get detected
    return 0;
}

然而这也有它的局限性,因为你应该避免毒害堆栈上的其他缓冲区/变量。在上面的简单例子中,做这样一个大的手动毒害是可以的,但不是一般的。

相关问题