Real-world examples of segmentation faults in out of bounds array access
Straightforward examples of segmentation faults in out of bounds array access
Let’s start with the most familiar pattern: a tiny off‑by‑one bug that ruins your day.
#include <stdio.h>
int main(void) {
int arr[5] = {1, 2, 3, 4, 5};
// Off-by-one: valid indices are 0..4
for (int i = 0; i <= 5; i++) {
printf("%d\n", arr[i]);
}
return 0;
}
On many platforms this prints five numbers, then crashes with a segmentation fault on the last iteration. This is a textbook example of segmentation faults in out of bounds array access: the loop condition i <= 5 allows i == 5, and arr[5] is past the end of the array.
What actually happens depends on the stack layout and compiler optimizations. Sometimes you just read garbage. Other times, you hit a guard page or overwrite something important and trigger a segfault. The nondeterministic feel of these crashes is exactly what makes them so frustrating.
Stack-based examples of segmentation faults in out of bounds array access
Stack arrays are fertile ground for mistakes. The stack sits right next to saved registers, frame pointers, and sometimes guard pages. When you want concrete examples of segmentation faults in out of bounds array access, stack buffers are where the drama happens.
Consider this function that tries to write a string into a fixed-size buffer:
#include <stdio.h>
#include <string.h>
void greet(const char *name) {
char buf[16];
// BUG: No length check
strcpy(buf, name); // out-of-bounds if name is long
printf("Hello, %s!\n", buf);
}
int main(void) {
greet("this-name-is-way-too-long-for-the-buffer");
return 0;
}
Here, buf has room for 15 characters plus a null terminator. strcpy doesn’t care; it copies until it hits \0. If the input is long enough, the write runs off the end of buf, into whatever sits next on the stack. On hardened systems with stack canaries and guard pages, that overflow often turns into an immediate segmentation fault.
This is more than a toy example. Stack-based overflows like this are a classic exploit path. Modern mitigations such as stack canaries, non-executable stacks, and address space layout randomization are well-documented by organizations like NIST and CISA (cisa.gov) and turn many of these out of bounds writes into detectable crashes instead of silent corruption.
Heap corruption examples of segmentation faults in out of bounds array access
The stack is noisy; the heap is subtle. Heap-based examples of segmentation faults in out of bounds array access often don’t crash immediately. Instead, they corrupt allocator metadata, and the segmentation fault appears much later in a completely different part of the program.
#include <stdlib.h>
#include <string.h>
int main(void) {
char *buf = malloc(16);
if (!buf) return 1;
// Out-of-bounds write by 10 bytes
memset(buf, 'A', 26); // writes past the allocated block
free(buf); // may crash here or later
return 0;
}
In this example, malloc(16) returns a 16-byte region, but memset writes 26 bytes. On many glibc-based Linux systems, this overwrites heap metadata stored adjacent to the allocation. The program might:
- Crash inside
freewith a segmentation fault. - Abort with a glibc error like
*** Error in ./a.out: free(): invalid next size (fast):. - Limp along for a while, then crash in a completely unrelated function.
Tools like AddressSanitizer (ASan), part of LLVM/Clang and GCC, have become standard in 2024–2025 CI pipelines precisely because they catch these heap-based examples of segmentation faults in out of bounds array access immediately, with precise stack traces and red-zone diagnostics. The LLVM project documents ASan in detail at llvm.org.
Multidimensional array example of segmentation faults in out of bounds array access
Things get more interesting once you go beyond one dimension. Miscalculating indices in a 2D array is a classic example of segmentation faults in out of bounds array access.
#include <stdio.h>
int main(void) {
int grid[3][4]; // 3 rows, 4 columns
// BUG: swapped indices, row index out of range
for (int col = 0; col < 4; col++) {
for (int row = 0; row <= 3; row++) { // should be row < 3
grid[col][row] = row + col; // col used as row
}
}
printf("%d\n", grid[0][0]);
return 0;
}
Two things go wrong:
- The loops conceptually treat
colas the first dimension androwas the second, but the array is declaredgrid[rows][cols]. - The inner loop allows
row == 3, which is out of bounds for the first dimension (0..2).
On many compilers, this layout means that grid[col][row] walks right off the end of the 3×4 block. Depending on what lies beyond, you might see:
- Immediate segmentation faults in debug builds.
- Silent data corruption in release builds.
This is why many teams in 2024–2025 default to higher-level containers (like std::vector<std::array<int,4>> in C++) or bounds-checked matrix libraries, even in performance-sensitive code.
Off-by-one errors at string boundaries
Strings are just arrays of bytes with a convention: terminate at \0. That convention makes them one of the best examples of segmentation faults in out of bounds array access, because the bug often hides in a single missing byte for the terminator.
#include <stdio.h>
int main(void) {
char s[5] = "abcd"; // fits exactly: 'a','b','c','d','\0'
// BUG: attempt to append one more character
s[4] = 'e'; // overwrites '\0'
s[5] = '\0'; // out-of-bounds write
printf("%s\n", s); // may read past the array
return 0;
}
Here, the array s has indices 0..4. The code tries to store 'e' at index 4 and then a null at index 5. That final write is outside the array and can corrupt adjacent stack memory. When printf walks the string looking for \0, it might:
- Read into uninitialized memory and print garbage.
- Cross into a protected page and trigger a segmentation fault.
This kind of off-by-one is a textbook example of segmentation faults in out of bounds array access that static analysis tools are increasingly good at spotting. Modern compilers and static analyzers (such as those used in safety-critical software per guidelines like MISRA C, documented at misra.org.uk) specifically warn about fixed-size buffers used for string operations.
Concurrency and race-driven examples
Not all out-of-bounds bugs are obvious in single-threaded code. In multi-threaded programs, a race condition can turn a logically valid index into an invalid one between the bounds check and the access.
#include <pthread.h>
#include <stdlib.h>
int *arr;
size_t arr_size;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *worker(void *arg) {
size_t i = (size_t)arg;
// Naive check without holding the lock
if (i < arr_size) {
// Another thread might shrink arr here
arr[i] = 42; // potential out-of-bounds write
}
return NULL;
}
int main(void) {
arr_size = 100;
arr = malloc(arr_size * sizeof(int));
// ... create threads that call worker ...
return 0;
}
If another thread can reallocate or shrink arr (or change arr_size) between the check and the write, arr[i] may become invalid. That can lead to a segmentation fault only under certain timing conditions, making it one of the more painful real examples of segmentation faults in out of bounds array access.
Tools like ThreadSanitizer (TSan) and ASan, which are widely integrated into CI pipelines by 2024–2025, help uncover these timing-dependent bugs by instrumenting memory accesses and detecting races and invalid indices.
Modern C++ example: safe and unsafe access side by side
C++ gives you both the gun and the safety. With std::vector, you can choose between unchecked operator[] and checked .at(). That contrast makes for a clean example of segmentation faults in out of bounds array access.
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3};
// Undefined behavior: no bounds check
int x = v[5]; // may silently read garbage or crash
// Defined behavior: throws std::out_of_range
try {
int y = v.at(5);
} catch (const std::out_of_range &e) {
std::cerr << "Caught exception: " << e.what() << '\n';
}
}
In practice, v[5] often reads past the end of the vector’s buffer. Whether you see a segmentation fault depends on the allocator and layout. With sanitizers enabled, this becomes a clear, debuggable example of segmentation faults in out of bounds array access; without them, it might silently corrupt memory.
By contrast, v.at(5) never segfaults; it throws an exception. Many modern C++ codebases in 2024–2025 default to .at() in safety-critical paths, and only use operator[] where profiling proves it matters.
Rust unsafe block example: when the compiler can’t save you
Rust’s safety guarantees stop at the boundary of unsafe. Inside, you can easily recreate the same examples of segmentation faults in out of bounds array access you see in C.
fn main() {
let arr = [1, 2, 3, 4, 5];
unsafe {
let p = arr.as_ptr().add(10); // out-of-bounds pointer
println!("{}", *p); // undefined behavior, may segfault
}
}
In safe Rust, arr[10] would panic at runtime with a clear message. In unsafe code, the compiler assumes you know what you’re doing. add(10) happily returns a pointer past the end of the array, and dereferencing it is undefined behavior. On many platforms, this is a clean segmentation fault.
This is a modern, very real example of segmentation faults in out of bounds array access in systems that are otherwise memory-safe. It’s a reminder that even in 2024–2025, when languages and tooling are better than ever, low-level mistakes still bite as soon as you bypass safety rails.
Why these examples still matter in 2024–2025
You might think that with memory-safe languages, advanced compilers, and hardened kernels, we’d be done with this class of bug. Reality says otherwise:
- Memory safety violations, including out-of-bounds accesses, still account for a large share of security vulnerabilities in C/C++ software, as highlighted repeatedly in reports from organizations like the U.S. Cybersecurity and Infrastructure Security Agency (cisa.gov).
- Microsoft and Google have both published analyses showing that a high percentage of their historical security bugs come from memory safety issues in native code; many of those are effectively examples of segmentation faults in out of bounds array access that were exploitable before mitigations.
- Tooling adoption is up. Sanitizers, fuzzers, and static analyzers are now standard in large projects, which means these bugs are more likely to show up in testing as clean, debuggable crashes instead of obscure production incidents.
The patterns, though, haven’t changed: off-by-one loops, mis-sized buffers, mismatched dimensions, and unsafe pointer math.
How to debug these examples of segmentation faults in out of bounds array access
When you hit a segfault that smells like an array bug, a few habits make life easier:
- Run under a debugger (like
gdborlldb) and inspect the backtrace. Look for any array or pointer access near the crash site. - Rebuild with AddressSanitizer enabled (
-fsanitize=addressin Clang/GCC). Many of the best examples of segmentation faults in out of bounds array access turn into crystal-clear ASan reports, with exact indices and stack traces. - Turn on extra warnings and static analysis (
-Wall -Wextra -Wpedantic, plus tools likeclang-tidy). These often flag suspicious loops and buffer sizes before runtime. - In C++, temporarily switch from
operator[]to.at()on vectors and arrays while debugging. - In Rust, avoid
unsafeuntil you’ve exhausted safe abstractions; when you must use it, keep unsafe blocks tiny and well-documented.
The more you internalize these real examples of segmentation faults in out of bounds array access, the faster you’ll recognize the pattern in your own code.
FAQ: common questions about out-of-bounds segmentation faults
Q: Can you give another simple example of a segmentation fault from out of bounds array access?
Yes. A very common pattern is iterating one step too far:
int arr[10];
for (int i = 0; i <= 10; i++) { // should be i < 10
arr[i] = i;
}
When i == 10, arr[10] is out of bounds. Sometimes this silently corrupts memory; other times it triggers a segmentation fault.
Q: Why do some out-of-bounds accesses not crash at all?
Because undefined behavior is undefined. You might land in memory that happens to be mapped and writable. The program keeps running with corrupted state, which is often worse than an immediate crash.
Q: How can I systematically find examples of segmentation faults in out of bounds array access in a legacy C codebase?
Combine static analysis (e.g., clang-tidy, cppcheck), dynamic tools like AddressSanitizer, and fuzzing. Fuzzers generate unexpected inputs that drive code down rarely tested paths, flushing out real examples of segmentation faults in out of bounds array access that manual testing misses.
Q: Are languages like Rust and Java completely free from these bugs?
In safe Rust and Java, normal array indexing is bounds-checked, so you get panics or exceptions instead of segmentation faults. But in Rust’s unsafe code or when interfacing with native libraries, you can still create the same examples of segmentation faults in out of bounds array access you see in C.
Q: What’s the best example of a defensive practice against these bugs?
In C and C++, prefer higher-level containers (std::vector, std::string), use functions that take explicit sizes (strncpy, memcpy_s), and make sanitizers part of your regular test runs. In Rust, stay in safe code as much as possible and isolate unsafe into small, audited modules.
Related Topics
Real examples of segmentation faults: Stack Overflow examples and how to fix them
Real-world examples of segmentation faults in out of bounds array access
Real‑world examples of segmentation fault in dynamic memory allocation
Real-world examples of segmentation fault examples: null pointer dereference
Explore More Segmentation Faults
Discover more examples and insights in this category.
View All Segmentation Faults