Recommended way to track down array out-of-bound access/write in C program

What is the recommended way to make sure that every access or write to array (allocated on stack) is actually valid (i.e. not provoking undefined behaviour) ?

What if use clang on Linux with the options -fsanitize=addressand -fsanitize=undefined? It is also available in gcc: http://gcc.gnu.org/gcc-4.8/changes.html.


clang with the option -fsanitize=undefined

This is an example:

#include <stdlib.h>

#define N 5

int main(int argc, char *argv[])
{
  int a[N] = {8, 1, 2, 3, 4}, i;

  int r =0;
  int end = atoi(argv[1]);
  for (int i = 0; i != end; ++i)
    r += a[i];

  return r;
}

Then

clang -fno-omit-frame-pointer -fsanitize=undefined -g out_boundary.c -o out_boundary_clang

$ ./out_boundary_clang 5
$ ./out_boundary_clang 6
out_boundary.c:12:10: runtime error: index 5 out of bounds for type 'int [5]'
Illegal instruction (core dumped)

And then analyze a core file

Program terminated with signal 4, Illegal instruction.
#0  main (argc=2, argv=0x7fff3a1c28c8) at out_boundary.c:12
12          r += a[i];
(gdb) p i
$1 = 5


clang with the option -fsanitize=address

This is a quote:

The tool can detect the following types of bugs:

* Out-of-bounds accesses to heap, stack and globals
* Use-after-free
* Use-after-return (to some extent)
* Double-free, invalid free
* Memory leaks (experimental)

clang -fno-omit-frame-pointer -fsanitize=address -g out_boundary.c -o out_boundary_clang

And then:

$ ./out_boundary_clang 6 2>&1 | asan_symbolize.py
=================================================================
==9634==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff91bb2ad4 at pc 0x459c67 bp 0x7fff91bb2910 sp 0x7fff91bb2908
READ of size 4 at 0x7fff91bb2ad4 thread T0
    #0 0x459c66 in main out_boundary.c:12
    #1 0x3a1d81ed1c in __libc_start_main ??:0
    #2 0x4594ac in _start ??:0
Address 0x7fff91bb2ad4 is located in stack of thread T0 at offset 244 in frame
    #0 0x45957f in main out_boundary.c:6
  This frame has 8 object(s):
    [32, 36) ''
    [96, 100) ''
    [160, 168) ''
    [224, 244) 'a'
    [288, 292) 'i'
    [352, 356) 'r'
    [416, 420) 'end'
    [480, 484) 'i1'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:
  0x10007236e500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007236e510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007236e520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007236e530: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
  0x10007236e540: 04 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f2 f2 f2 f2
=>0x10007236e550: 00 f4 f4 f4 f2 f2 f2 f2 00 00[04]f4 f2 f2 f2 f2
  0x10007236e560: 04 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f2 f2 f2 f2
  0x10007236e570: 04 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f3 f3 f3 f3
  0x10007236e580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007236e590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007236e5a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
  ASan internal:         fe
==9634==ABORTING

Or you can use both this options. Useful links:

Leave a Comment