Home > Enterprise >  Address sanitizer does not detect out-of-bounds
Address sanitizer does not detect out-of-bounds

Time:11-06

I'm learning about address sanitizers:

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

But I can't trigger any error / warning:

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

CodePudding user response:

In the case of -fsanitize=address, memory accesses are checked using a shadow memory of limited size, where stack buffers are surrounded by two redzones (one before and one after the buffer). If an access happens inside a redzone Asan detects the bug and aborts execution. However, those redzones are quite small, as they are designed to detect buffer overrun/overflow bugs, and not wild out-of-bound accesses with large indexes.

So what's happening in your case is something like this:

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

And the buggy memory read goes undetected.

What you want to detect this kind of errors is -fsanitize=undefined, or more specifically one of -fsanitize=bounds or -fsanitize=bounds-strict. In such case the bound check is done taking into account the definition of the array and its type and bounds are checked for every access through its identifier.

$ 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

Note however that this does not usually save you from doing something like:

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

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

Because in this case the bounds of arr cannot be known inside the function foo.


Alternatively you could also consider manually poisoning shadow memory:

#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;
}

However this also has its limitations, because you should avoid poisoning other buffers/variables on the stack. In the simple example above doing such a large manual poisoning could be ok, but not in general.

  • Related