Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do compilers not warn about out-of-bounds static array indices?

Tags:

A colleague of mine recently got bitten badly by writing out of bounds to a static array on the stack (he added an element to it without increasing the array size). Shouldn't the compiler catch this kind of error? The following code compiles cleanly with gcc, even with the -Wall -Wextra options, and yet it is clearly erroneous:

int main(void)
{
  int a[10];
  a[13] = 3;  // oops, overwrote the return address
  return 0;
}

I'm positive that this is undefined behavior, although I can't find an excerpt from the C99 standard saying so at the moment. But in the simplest case, where the size of an array is known as compile time and the indices are known at compile time, shouldn't the compiler emit a warning at the very least?

like image 504
Adam Rosenfield Avatar asked Dec 20 '08 06:12

Adam Rosenfield


People also ask

Does compiler check array bounds?

No. C doesn't have bounds checking for arrays; it's entirely up to the user to ensure they're within the bounds of an array.

Does c check array bounds?

C doesn't check array index out of bound.

What is the compiler option used for checking array bounds?

Answer. In VisualAge C++ V6 for AIX, you can request array bounds checking to be performed at run time by using the -qcheck compiler option. The compiler inserts additional code (in the object module) that raises a signal when an attempt to access an array beyond bounds is detected.


2 Answers

GCC does warn about this. But you need to do two things:

  1. Enable optimization. Without at least -O2, GCC is not doing enough analysis to know what a is, and that you ran off the edge.
  2. Change your example so that a[] is actually used, otherwise GCC generates a no-op program and has completely discarded your assignment.

.

$ cat foo.c 
int main(void)
{
  int a[10];
  a[13] = 3;  // oops, overwrote the return address
  return a[1];
}
$ gcc -Wall -Wextra  -O2 -c foo.c 
foo.c: In function ‘main’:
foo.c:4: warning: array subscript is above array bounds

BTW: If you returned a[13] in your test program, that wouldn't work either, as GCC optimizes out the array again.

like image 173
derobert Avatar answered Oct 01 '22 22:10

derobert


Have you tried -fmudflap with GCC? These are runtime checks but are useful, as most often you have got to do with runtime calculated indices anyway. Instead of silently continue to work, it will notify you about those bugs.

-fmudflap -fmudflapth -fmudflapir For front-ends that support it (C and C++), instrument all risky pointer/array dereferencing operations, some standard library string/heap functions, and some other associated constructs with range/validity tests. Modules so instrumented should be immune to buffer overflows, invalid heap use, and some other classes of C/C++ programming errors. The instrumen‐ tation relies on a separate runtime library (libmudflap), which will be linked into a program if -fmudflap is given at link time. Run-time behavior of the instrumented program is controlled by the MUDFLAP_OPTIONS environment variable. See "env MUDFLAP_OPTIONS=-help a.out" for its options.

Use -fmudflapth instead of -fmudflap to compile and to link if your program is multi-threaded. Use -fmudflapir, in addition to -fmudflap or -fmudflapth, if instrumentation should ignore pointer reads. This produces less instrumentation (and there‐ fore faster execution) and still provides some protection against outright memory corrupting writes, but allows erroneously read data to propagate within a program.

Here is what mudflap gives me for your example:

[js@HOST2 cpp]$ gcc -fstack-protector-all -fmudflap -lmudflap mudf.c        
[js@HOST2 cpp]$ ./a.out
*******
mudflap violation 1 (check/write): time=1229801723.191441 ptr=0xbfdd9c04 size=56
pc=0xb7fb126d location=`mudf.c:4:3 (main)'
      /usr/lib/libmudflap.so.0(__mf_check+0x3d) [0xb7fb126d]
      ./a.out(main+0xb9) [0x804887d]
      /usr/lib/libmudflap.so.0(__wrap_main+0x4f) [0xb7fb0a5f]
Nearby object 1: checked region begins 0B into and ends 16B after
mudflap object 0x8509cd8: name=`mudf.c:3:7 (main) a'
bounds=[0xbfdd9c04,0xbfdd9c2b] size=40 area=stack check=0r/3w liveness=3
alloc time=1229801723.191433 pc=0xb7fb09fd
number of nearby objects: 1
[js@HOST2 cpp]$

It has a bunch of options. For example it can fork off a gdb process upon violations, can show you where your program leaked (using -print-leaks) or detect uninitialized variable reads. Use MUDFLAP_OPTIONS=-help ./a.out to get a list of options. Since mudflap only outputs addresses and not filenames and lines of the source, i wrote a little gawk script:

/^ / {
    file = gensub(/([^(]*).*/, "\\1", 1);
    addr = gensub(/.*\[([x[:xdigit:]]*)\]$/, "\\1", 1);
    if(file && addr) {
        cmd = "addr2line -e " file " " addr
        cmd | getline laddr
        print $0 " (" laddr ")"
        close (cmd)
        next;
    }
}

1 # print all other lines

Pipe the output of mudflap into it, and it will display the sourcefile and line of each backtrace entry.

Also -fstack-protector[-all] :

-fstack-protector Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits.

-fstack-protector-all Like -fstack-protector except that all functions are protected.

like image 35
Johannes Schaub - litb Avatar answered Oct 01 '22 23:10

Johannes Schaub - litb