Question
I am running my compiled C program (./a.out). It executes for some time and then terminates with an error similar to the following:
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)
Aborted
What can cause this message, and how can I find and correct the problem?
Short Answer
This error usually means your program wrote beyond the bounds of a local variable on the stack, often a fixed-size array. You will learn how stack canaries detect corruption, how to locate the faulty write with compiler warnings and sanitizers, and how to prevent it with bounds-aware code.
Concept
A stack smashing detected message is a runtime safety check reporting likely memory corruption.
In C, a function commonly stores local variables on the stack. For example:
void read_name(void) {
char name[16];
}
The name array can hold at most 15 visible characters plus the terminating \0 character. If code writes past that boundary, it can overwrite nearby stack data.
Modern compilers can insert a hidden value called a stack canary near vulnerable local variables. Before the function returns, generated code checks whether that value changed. If it changed, the runtime assumes an out-of-bounds write occurred and aborts the program rather than continuing with corrupted control data.
The message does not necessarily identify the line where the bad write happened. The overflow may occur earlier, and it may only be detected when the affected function returns.
Typical causes include:
- Writing past the end of a local array.
- Using
strcpy,sprintf, orgetswith insufficient destination space. - Passing an incorrect size to
memcpy,snprintf,read, or similar functions. - Forgetting space for a string's final null terminator.
- Using an invalid array index, including a negative index.
- Incorrectly handling formatted input with
scanf.
This matters because out-of-bounds writes cause undefined behavior. A crash is one possible result; silent data corruption or a security vulnerability can also result.
Mental Model
Imagine a function's stack frame as a desk with labelled trays:
- one tray holds
name[16]; - another holds other local data;
- a sealed alarm strip, the canary, sits near the edge.
Writing name[20] is like putting papers into a 16-slot tray and continuing onto the desk. The program may appear to work until it checks the alarm strip while leaving the function. If the strip has been disturbed, it stops immediately and reports stack smashing.
The important idea is that the failure is often detected after the mistaken write, not exactly at the write itself.
Syntax and Examples
A fixed-size C array has a strict valid index range.
char code[8];
Valid indexes are 0 through 7. A C string stored in code needs one byte for \0, so it can contain at most 7 visible characters.
Unsafe copy
#include <string.h>
int main(void) {
char code[8];
strcpy(code, "ABCDEFGH"); // Needs 9 bytes including '\0'
return 0;
}
"ABCDEFGH" has 8 characters, but a C string also needs a terminating null byte. strcpy writes 9 bytes into an 8-byte array.
Safer formatted copy
#include <stdio.h>
int main(void) {
char code[8];
const char *source = "ABCDEFGH";
int written = snprintf(code, sizeof code, "%s", source);
if (written < 0) {
return 1;
}
if ((size_t)written >= sizeof code) {
puts("Input was too long and was truncated.");
}
printf("%s\n", code);
return 0;
}
sizeof code evaluates to the capacity of the array in this scope. snprintf writes at most sizeof code - 1 visible characters and terminates the result when the buffer size is greater than zero. Its return value lets the program detect truncation.
For input, specify a maximum field width:
char name[20];
if (scanf("%19s", name) == 1) {
printf("Hello, %s\n", name);
}
The width is one less than the array capacity so there is room for \0. Prefer fgets when input can contain spaces.
Step by Step Execution
Consider this incorrect example:
#include <string.h>
void set_label(const char *input) {
char label[5];
strcpy(label, input);
}
int main(void) {
set_label("orange");
return 0;
}
Step by step:
maincallsset_labelwith the string"orange".set_labelcreateslabel, which has room for only 5 bytes.- The string
"orange"requires 7 bytes: six letters plus\0. strcpycopies all 7 bytes because it has no destination-size argument.- The final two writes go beyond
labeland may overwrite nearby stack memory, including the canary. - When
set_labelreturns, the compiler-generated canary check runs. - If the canary differs from its original value, the process aborts with
stack smashing detected.
A corrected version defines a clear truncation policy:
#include <stdio.h>
void set_label(const char *input) {
char label[5];
snprintf(label, sizeof label, "%s", input);
printf("Label: %s\n", label);
}
This prints oran. In a real application, you may instead reject input that does not fit.
Real World Use Cases
Bounds checking is needed whenever data enters a fixed-capacity buffer.
- Command-line tools: Copying command-line arguments into local arrays.
- Network services: Reading request headers, packet fields, or usernames supplied by clients.
- File processing: Parsing lines, CSV fields, or binary file records.
- Embedded systems: Storing sensor labels and messages in small fixed buffers.
- Logging: Formatting a message before writing it to a log file.
- User interfaces: Accepting names, passwords, and form fields with a known maximum length.
In each case, decide what should happen when data is too long: reject it, truncate it safely, allocate enough memory dynamically, or process it in chunks.
Real Codebase Usage
Real C codebases reduce stack-overflow risks through a combination of design, validation, and tooling.
Validate length before copying
#include <stdbool.h>
#include <string.h>
bool copy_username(char destination[32], const char *source) {
size_t length = strlen(source);
if (length >= 32) {
return false;
}
memcpy(destination, source, length + 1);
return true;
}
The guard clause rejects a value that cannot fit, including its terminator.
Pass capacity with buffers
A function receiving an array parameter cannot automatically know the caller's array size. Pass it explicitly:
#include <stdio.h>
int format_path(char *buffer, size_t capacity, const char *file) {
int result = snprintf(buffer, capacity, "/tmp/%s", file);
if (result < 0 || (size_t)result >= capacity) {
return -1;
}
return 0;
}
This pattern makes the buffer size part of the function contract.
Use diagnostic builds
During development, compile with warnings and runtime instrumentation:
gcc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -g \
-fsanitize=address,undefined -fno-omit-frame-pointer program.c -o program
./program
AddressSanitizer often reports the invalid write at or close to the source line that caused it. For a debugging build, it is also useful to enable stack protection explicitly:
gcc -g -fstack-protector-strong program.c -o program
Compiler options can differ by compiler and platform, so consult your compiler's documentation when integrating them into a build system.
Common Mistakes
Assuming strncpy always produces a valid string
char destination[4];
strncpy(destination, "hello", sizeof destination);
printf("%s\n", destination); // Undefined behavior: may not end in '\0'
strncpy does not guarantee null termination when the source is as long as or longer than the limit. Prefer snprintf, or explicitly terminate after a carefully chosen copy.
Using sizeof on an array parameter
void clear(char buffer[]) {
memset(buffer, 0, sizeof buffer); // Wrong: buffer is a pointer here.
}
Inside the function, buffer has decayed to a pointer, so sizeof buffer is the pointer size, not the caller's array capacity. Pass the size separately.
void clear(char *buffer, size_t capacity) {
memset(buffer, 0, capacity);
}
Giving scanf no width limit
char name[20];
scanf("%s", name); // Unsafe for long input
Use a width that leaves room for the terminator:
scanf("%19s", name);
Confusing byte count with element count
int values[10];
memcpy(values, source, 10); // Copies 10 bytes, not 10 integers
Use the required byte count:
memcpy(values, source, sizeof values);
Disabling the stack protector instead of fixing the bug
Turning off stack protection may hide the immediate message, but the invalid write and undefined behavior remain. Use the report as a signal to locate and correct the out-of-bounds access.
Comparisons
| Approach | What it does | Main concern | Typical use |
|---|---|---|---|
strcpy(destination, source) | Copies until \0 | Does not know destination capacity | Avoid for untrusted or variable-length input |
sprintf(buffer, ...) | Formats text into a buffer | Does not know buffer capacity | Usually replace with snprintf |
snprintf(buffer, size, ...) | Formats with a byte limit | Output can be truncated; check return value | Bounded text formatting |
strncpy(destination, source, n) | Copies up to n bytes | May omit \0; can pad unnecessarily | Use only when its exact semantics are intended |
fgets(buffer, size, stream) | Reads a bounded line | May retain newline; detects input only up to capacity | Interactive and file line input |
| Stack canary | Detects some stack corruption | Detects after corruption; does not prevent every memory bug | Compiler runtime protection |
| AddressSanitizer | Detects many invalid memory accesses | Development/testing tool with runtime overhead | Finding the source of memory errors |
A stack canary is a safety alarm, not a replacement for checking bounds. The best fix is always to ensure every write stays within the intended object.
Cheat Sheet
char text[N];has valid indexes from0toN - 1.- A C string needs one extra byte for
\0. - For an array in the same scope, use
sizeof arrayas its capacity. - For a pointer parameter, pass capacity separately:
func(char *buffer, size_t capacity). - Prefer
snprintf(buffer, sizeof buffer, ...)oversprintf. - Avoid unbounded
strcpy,sprintf, andscanf("%s", buffer). - Use
scanf("%Ns", buffer)withN = capacity - 1, or preferfgets. - Check
snprintf: truncation occurred when its nonnegative return value is>= capacity. - Compile debugging builds with
-Wall -Wextra -g -fsanitize=address,undefined. - A stack-smashing message usually means the corrupted function is being exited; inspect writes made earlier in that function.
FAQ
What does “stack smashing detected” mean in C?
It usually means a stack-protector canary was modified, most often because code wrote outside a local stack array.
Is stack smashing always caused by strcpy?
No. Any out-of-bounds write can cause it, including bad indexes, oversized memcpy calls, unsafe scanf, and incorrect length calculations.
Why does the error occur after my program has been running?
The bad write may happen earlier. The canary is commonly checked when a function returns, so detection can be delayed.
Does the backtrace point to the bug in my code?
Not necessarily. Functions such as __fortify_fail are reporting the failed safety check. Use a debugger or AddressSanitizer to find the original invalid write.
Can I fix the error by increasing the array size?
Only if the larger size is a deliberate, valid limit and all inputs are still checked. Blindly increasing an array can hide a logic error and does not safely handle unlimited input.
Should I disable stack protection?
No. Disabling it removes a diagnostic and protection mechanism, but does not repair the memory corruption.
How can I find the exact line causing the overflow?
Rebuild with debug symbols and AddressSanitizer, run the same failing input, and inspect the sanitizer report. Also enable compiler warnings and review all writes to local arrays.
Mini Project
Description
Build a small command-line greeting formatter that reads a name into a fixed-size buffer and safely creates a greeting. It demonstrates bounded input, bounded formatting, and explicit handling of input that does not fit.
Goal
Read a name safely and print a greeting without writing beyond any buffer.
Requirements
Use fgets to read the name into a fixed-size character array.
Remove the trailing newline when one is present.
Reject an input line that is longer than the available input buffer.
Format the greeting with snprintf.
Detect and report greeting truncation.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.