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.