// 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;
}
#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
nitems has indexes0ton - 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;
}
#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;
}
#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.