Home/Blog/C

// C

Array index out of bounds in C: find the line that does it

C never checks an array index. Ask for a[5] from an array of two and the program reads whatever happens to sit in memory after it. Sometimes that prints a strange number, sometimes it crashes much later with a segmentation fault, and sometimes it seems to work, which is the worst outcome of the three.

The fix is to find the line that reads past the end. The fastest way is to build with the undefined-behaviour sanitizer, which stops the program on that line and names it. Our free C compiler runs every program that way, so the examples below show exactly what you get.

What the bug looks like

This program has two numbers and then asks for the sixth:

#include <stdio.h>

int main(void) {
    int scores[2] = {90, 75};
    int i = 5;
    printf("start\n");
    printf("%d\n", scores[i]);
    return 0;
}

Output

start
main.c:7: array index out of bounds

The program got as far as start, and then stopped on line 7, the line that reads scores[i] with i equal to 5. Without the sanitizer, the same program would print a number nobody put there and carry on.

The usual causes

Almost every out-of-bounds read comes from one of three habits.

  • Counting to the length instead of below it. An array of n items has indexes 0 to n - 1, so a loop written with <= reads one past the end on its last pass.
  • Using a length from somewhere else. The array changed size, the loop limit did not.
  • Trusting input. An index read from the user or a file was never checked against the array’s size.

The first one is the most common by far. Here it is in a loop:

#include <stdio.h>

int main(void) {
    int scores[3] = {90, 75, 60};
    int total = 0;
    for (int i = 0; i <= 3; i++) {
        total += scores[i];
    }
    printf("%d\n", total);
    return 0;
}

Output

main.c:7: array index out of bounds

The fix

Loop below the length, and take the length from the array itself so the two cannot drift apart:

#include <stdio.h>

int main(void) {
    int scores[3] = {90, 75, 60};
    int count = sizeof scores / sizeof scores[0];
    int total = 0;
    for (int i = 0; i < count; i++) {
        total += scores[i];
    }
    printf("%d\n", total);
    return 0;
}

Output

225

sizeof scores / sizeof scores[0] is the number of items in an array declared in the same function. It does not work on a pointer, so a function that receives an array needs its length passed in as a second argument.

Catching it on your own machine

The checks that stopped these programs come with gcc and clang. Build with the sanitizer turned on:

gcc -g -fsanitize=address,undefined main.c -o main
./main

address catches reads and writes past the end of arrays on the heap and the stack, and undefined catches many other kinds of undefined behaviour, such as signed integer overflow, though not all of them. Keep them on while you develop and off in the build you ship. Our compiler page uses the undefined-behaviour checks only, so a read past the end of memory from malloc, or through a pointer, is not caught there.