Consider this simple program:
#include <array>
#include <iostream>
#include <cstdlib>
int main(int argc, char* argv[]) {
std::array<int, 5> arr = {0, 1, 2, 3, 4};
int idx = std::atoi(argv[1]);
int val = std::atoi(argv[2]);
arr[idx] = val;
for (auto i=0u; i <= idx; i++) {
std::cout << "arr[" << i << "] = " << arr[i] << std::endl;
}
}
If I compile and link it with GCC 6.3.1 like this:
g++ -O0 -std=gnu++14 -fsanitize=undefined example.cpp
and run it like this:
a.out 5 98
I don't get any warnings, even though I am writing and reading one element past the end of 'arr' (the array ends at index 4).
If I run:
a.out 6 98
I receive a warning that 'index 6 out of bounds for type 'int [5]'. In this case I am writing and reading two elements past the end of 'arr'.
Why doesn't the off-by-one case throw an error? I guess because one element past the end of an array is a valid memory address (i.e. iterators can point to it?). Can you suggest any other tool that can reliably detect out of bounds access by one element?
EDIT
I can also run:
g++ -O0 -std=gnu++14 -fsanitize=bounds-strict example.cpp
And I see the same behaviour, i.e. out of bounds access by one element does not trigger a warning, but, out of bounds by two elements does. This is the part I'm trying to understand.
Why doesn't the off-by-one case throw an error?
Because -fsanitize=undefined
doesn't detect out of bounds accesses.
Can you suggest any other tool that can reliably detect out of bounds access by one element?
I suggest gcc
with -fsanitize=address
.
$ g++ -O0 -std=gnu++14 -fsanitize=address 1.cpp
$ ./a.out 6 98
=================================================================
==119887==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd3b1aa4b8 at pc 0x560be273c4e9 bp 0x7ffd3b1aa450 sp 0x7ffd3b1aa440
WRITE of size 4 at 0x7ffd3b1aa4b8 thread T0
#0 0x560be273c4e8 in main (/tmp/a.out+0x14e8)
#1 0x7f9dafafe001 in __libc_start_main (/usr/lib/libc.so.6+0x27001)
#2 0x560be273c17d in _start (/tmp/a.out+0x117d)
...
The options are documented in gcc instrumentations options.
Side note: In one of my projects, I use -fsanitize=address -fsanitize=undefined -fsanitize=leak -fsanitize=pointer-subtract -fsanitize=pointer-compare -fno-omit-frame-pointer -fstack-protector-all -fstack-clash-protection -fcf-protection
.