Skip to content

Fix: Segmentation fault (core dumped) in Linux / C / C++

FixDevs ·

Quick Answer

How to fix the 'Segmentation fault (core dumped)' error in Linux, covering null pointer dereference, buffer overflow, use-after-free, stack overflow, and debugging with GDB, Valgrind, and AddressSanitizer.

The Error

You run a compiled program and it crashes immediately with:

Segmentation fault (core dumped)

Or you see the shorter variant:

Segmentation fault

If you’re running a Python script that uses C extensions, you might see:

$ python my_script.py
Segmentation fault (core dumped)

The program terminates, and if core dumps are enabled, the kernel writes a core file to disk. Either way, nothing useful appears on screen — just that one cryptic line.

This error means your program tried to access a memory address it is not allowed to touch. The operating system kills it with signal 11 (SIGSEGV) to prevent damage.

Why This Happens

A segmentation fault occurs when a process attempts to read from or write to a memory address that the operating system has not mapped to it. The CPU’s memory management unit (MMU) detects the illegal access and raises a hardware exception. Linux translates this into SIGSEGV, which terminates the process by default.

The most common root causes are:

  • Null pointer dereference — accessing memory through a pointer that is NULL (address 0x0).
  • Buffer overflow — writing past the end of an array or buffer, corrupting adjacent memory or hitting an unmapped page.
  • Use-after-free — accessing memory that has already been freed with free() or delete.
  • Stack overflow — infinite or extremely deep recursion exhausts the stack, and the next function call writes beyond the stack guard page. If you hit recursive depth issues in Python specifically, see the guide on fixing Python RecursionError.
  • Writing to read-only memory — modifying a string literal or a const-qualified region that the linker placed in a read-only segment.
  • Misaligned access — on some architectures, accessing data at an address that is not properly aligned for the data type.
  • Dangling pointers — using a pointer to a local variable after the function that owned it has returned.

Understanding which of these caused your crash is the key to fixing it. The sections below walk through each cause and its fix, plus the debugging tools that pinpoint the exact line.

Fix 1: Enable Core Dumps and Analyze Them

By default, many Linux distributions disable core dumps or limit their size. Without a core file, you lose the snapshot of your program’s state at the moment of the crash.

Enable core dumps for the current shell session:

ulimit -c unlimited

Then reproduce the crash. A file named core or core.<pid> appears in the current directory (or wherever your system is configured to write cores).

Note: Some systems redirect core dumps via /proc/sys/kernel/core_pattern. Check it:

cat /proc/sys/kernel/core_pattern

If it shows a path like |/usr/lib/systemd/systemd-coredump, the core is managed by systemd. List recent cores with:

coredumpctl list

And debug the latest one with:

coredumpctl debug

Analyze the Core File with GDB

If you have a traditional core file, load it into GDB:

gdb ./your_program core

GDB drops you at the exact instruction that caused the fault. Run bt (backtrace) to see the full call stack:

(gdb) bt
#0  0x0000555555555149 in process_data (ptr=0x0) at main.c:12
#1  0x00005555555551a2 in main () at main.c:25

This tells you the crash happened at main.c:12 inside process_data, and the pointer ptr was 0x0 — a null pointer dereference.

Pro Tip: Compile your program with debug symbols (-g flag) before reproducing the crash. Without symbols, GDB shows raw addresses instead of function names and line numbers. Use gcc -g -o your_program your_program.c or add -g to your CFLAGS/CXXFLAGS in your build system.

Key GDB Commands for Segfault Debugging

CommandWhat It Does
btFull backtrace
bt fullBacktrace with local variable values
frame NSwitch to stack frame N
print varPrint a variable’s value
info registersShow CPU register values
x/10x $rspExamine 10 hex words at the stack pointer
listShow source code around the crash point

Fix 2: Null Pointer Dereference

This is the single most common cause of segfaults. It happens when you dereference a pointer that is NULL:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *data = NULL;
    printf("%c\n", data[0]);  // SIGSEGV: dereferencing NULL
    return 0;
}

How to Fix It

Add null checks before every pointer dereference, especially after functions that can return NULL:

char *data = get_data();
if (data == NULL) {
    fprintf(stderr, "Error: get_data() returned NULL\n");
    return 1;
}
printf("%c\n", data[0]);  // Safe

Common functions that return NULL on failure:

  • malloc(), calloc(), realloc()
  • fopen()
  • getenv()
  • strstr(), strchr()
  • Any function documented as returning NULL on error

Common Mistake: Assuming malloc() never fails. On systems with memory overcommit (most Linux desktops), malloc() rarely returns NULL — it returns a pointer, and the kernel kills the process later when physical memory runs out (the OOM killer). But on embedded systems, containers with memory limits, or when allocating very large blocks, malloc() absolutely can return NULL. Always check. If you’re running into OOM kills in containers, see Docker Exited (137) OOMKilled.

Fix 3: Buffer Overflow

Writing beyond the bounds of an array corrupts memory. If you’re lucky, it hits an unmapped page and you get an immediate segfault. If you’re unlucky, it silently corrupts other data and crashes later in an unrelated function.

#include <string.h>

int main() {
    char buffer[10];
    strcpy(buffer, "This string is way too long for the buffer");
    // Writes 44 bytes into a 10-byte buffer → stack corruption → segfault
    return 0;
}

How to Fix It

Use size-bounded functions instead of their unbounded counterparts:

DangerousSafe Alternative
strcpy()strncpy() or snprintf()
strcat()strncat()
sprintf()snprintf()
gets()fgets()
scanf("%s", buf)scanf("%9s", buf) (with width specifier)

Fixed version:

#include <stdio.h>
#include <string.h>

int main() {
    char buffer[10];
    snprintf(buffer, sizeof(buffer), "This string is way too long for the buffer");
    // Truncates safely to 9 chars + null terminator
    printf("%s\n", buffer);
    return 0;
}

For dynamic data, calculate the required size and allocate accordingly:

const char *input = get_user_input();
size_t len = strlen(input);
char *buffer = malloc(len + 1);
if (buffer == NULL) {
    perror("malloc");
    return 1;
}
memcpy(buffer, input, len + 1);

Detect Buffer Overflows with AddressSanitizer

Compile with AddressSanitizer (ASan) to catch buffer overflows at runtime:

gcc -g -fsanitize=address -o your_program your_program.c
./your_program

ASan prints a detailed report showing exactly where the overflow happened:

==12345==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd12345678
WRITE of size 44 at 0x7ffd12345678 thread T0
    #0 0x555555555123 in main /home/user/main.c:5

ASan is available in GCC (4.8+) and Clang (3.1+). It adds ~2x runtime overhead, so use it during development and testing, not in production.

Fix 4: Use-After-Free

Accessing memory after calling free() is undefined behavior. The memory may have been returned to the OS (segfault) or reallocated for something else (silent corruption):

#include <stdlib.h>
#include <stdio.h>

int main() {
    int *nums = malloc(5 * sizeof(int));
    nums[0] = 42;
    free(nums);
    printf("%d\n", nums[0]);  // Use-after-free → undefined behavior / segfault
    return 0;
}

How to Fix It

Set pointers to NULL immediately after freeing them. This turns a silent use-after-free into an obvious null pointer dereference, which is easier to debug:

free(nums);
nums = NULL;  // Any later access through nums now crashes immediately at address 0x0

For more structural fixes:

  • Clarify ownership. Decide which part of your code “owns” each allocation and is responsible for freeing it. Document this in comments.
  • Avoid returning pointers to freed memory. If a function frees a struct, make sure no other code holds a reference to it.
  • Use Valgrind to detect use-after-free errors automatically (covered below).

In C++, prefer smart pointers (std::unique_ptr, std::shared_ptr) over raw new/delete. They free memory automatically when it goes out of scope and prevent most use-after-free bugs.

Fix 5: Stack Overflow from Deep Recursion

Every function call pushes a stack frame. If recursion goes too deep, the stack grows past its limit (typically 8 MB on Linux) and hits the guard page:

#include <stdio.h>

void recurse(int n) {
    int large_array[1024];  // 4 KB per frame
    printf("Depth: %d\n", n);
    recurse(n + 1);  // No base case → infinite recursion
}

int main() {
    recurse(0);
    return 0;
}

How to Fix It

  1. Add a base case to your recursion:
void recurse(int n) {
    if (n >= 1000) return;  // Base case
    int large_array[1024];
    recurse(n + 1);
}
  1. Convert to iteration if the recursion depth is unbounded:
void process_iteratively() {
    for (int i = 0; i < max_depth; i++) {
        // Do the work without recursive calls
    }
}
  1. Increase the stack size if your algorithm genuinely needs deep recursion:
ulimit -s 32768  # Set stack size to 32 MB (value is in KB)

Or set it per-thread with pthread_attr_setstacksize().

Check the current stack size limit:

ulimit -s

This prints the limit in KB. The default is usually 8192 (8 MB).

If your program does heavy recursion in Python, the segfault might come from exceeding the C-level stack rather than Python’s recursion limit. See the Python RecursionError guide for details on that interaction.

Fix 6: Debug with Valgrind

Valgrind runs your program in a virtual environment that tracks every memory operation. It catches issues that AddressSanitizer misses and vice versa — they’re complementary tools.

valgrind --leak-check=full --track-origins=yes ./your_program

Valgrind reports errors like:

==54321== Invalid read of size 4
==54321==    at 0x1091A2: process_data (main.c:18)
==54321==    by 0x109210: main (main.c:31)
==54321==  Address 0x4a3c050 is 0 bytes inside a block of size 20 free'd
==54321==    at 0x483CA3F: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==54321==    by 0x109198: cleanup (main.c:14)

This tells you that main.c:18 reads from memory that was freed at main.c:14 — a use-after-free.

Key Valgrind Flags

FlagPurpose
--leak-check=fullReport details of memory leaks
--track-origins=yesShow where uninitialized values came from
--show-reachable=yesReport memory that’s still reachable but not freed
--log-file=valgrind.logWrite output to a file
--suppressions=file.suppSuppress known false positives

Note: Valgrind slows your program by 10-50x. It’s a development tool, not a production monitor.

Valgrind vs. AddressSanitizer

FeatureValgrindAddressSanitizer
Speed overhead10-50x~2x
Recompilation neededNoYes (-fsanitize=address)
Use-after-freeYesYes
Buffer overflowYesYes (better for stack overflows)
Uninitialized readsYes (--track-origins)No (use MemorySanitizer)
Memory leaksYesYes (LeakSanitizer, enabled by default with ASan)

Use ASan during daily development (it’s fast). Use Valgrind for deeper analysis when ASan doesn’t catch the bug or when you can’t recompile.

Fix 7: Segfaults in Python C Extensions

Python itself is memory-safe, but C extensions (NumPy, Pandas, OpenCV, compiled Cython code) can segfault. When you see:

$ python my_script.py
Segmentation fault (core dumped)

The crash is almost always in a C extension, not in your Python code.

How to Debug It

  1. Run Python under GDB:
gdb python
(gdb) run my_script.py

When it crashes, use bt to see which C function caused the fault.

  1. Use the faulthandler module (Python 3.3+):
import faulthandler
faulthandler.enable()

# Your code here

Or enable it from the command line:

python -X faulthandler my_script.py

This prints a Python traceback on segfault, showing which Python-level call triggered the crash.

  1. Common causes and fixes:
  • Version mismatch: Your C extension was compiled against a different Python version or NumPy version. Fix: reinstall the package with pip install --force-reinstall --no-cache-dir <package>.
  • Corrupted installation: Fix: create a fresh virtual environment and reinstall dependencies.
  • Bug in the extension: Check the library’s GitHub issues for known segfaults. Update to the latest version.
  • Thread safety: Some C extensions are not thread-safe. If you’re using threading, try switching to multiprocessing or adding locks.

Fix 8: Use AddressSanitizer for Comprehensive Detection

AddressSanitizer is a compiler-based tool that instruments your code to detect memory errors at runtime. It catches more than just the errors that cause immediate segfaults — it finds bugs that silently corrupt memory and would crash later.

What ASan Detects

  • Stack buffer overflow
  • Heap buffer overflow
  • Global buffer overflow
  • Use-after-free
  • Use-after-return (with ASAN_OPTIONS=detect_stack_use_after_return=1)
  • Double-free
  • Memory leaks (via integrated LeakSanitizer)

How to Use It

Compile with ASan flags:

# GCC
gcc -g -fsanitize=address -fno-omit-frame-pointer -o program program.c

# Clang
clang -g -fsanitize=address -fno-omit-frame-pointer -o program program.c

# C++ (either compiler)
g++ -g -fsanitize=address -fno-omit-frame-pointer -o program program.cpp

The -fno-omit-frame-pointer flag ensures ASan can produce accurate stack traces.

Run the program normally:

./program

ASan automatically intercepts memory operations and reports violations with full stack traces.

Tuning ASan Behavior

Control ASan via the ASAN_OPTIONS environment variable:

ASAN_OPTIONS=halt_on_error=0:detect_leaks=1:print_stats=1 ./program

Useful options:

OptionEffect
halt_on_error=0Continue after first error (default: halt)
detect_leaks=1Enable leak detection
detect_stack_use_after_return=1Detect dangling stack references
check_initialization_order=1Detect C++ static init order bugs

Combine with Other Sanitizers

For even deeper analysis, use UndefinedBehaviorSanitizer alongside ASan:

gcc -g -fsanitize=address,undefined -fno-omit-frame-pointer -o program program.c

This catches integer overflow, null pointer dereference through different code paths, and other undefined behavior that might not trigger a segfault but indicates bugs.

Fix 9: Writing to Read-Only Memory

String literals in C are stored in a read-only memory segment. Modifying them causes a segfault:

int main() {
    char *str = "hello";  // Points to read-only memory
    str[0] = 'H';          // SIGSEGV: writing to read-only segment
    return 0;
}

How to Fix It

Use an array instead of a pointer to create a mutable copy:

int main() {
    char str[] = "hello";  // Copies the string to the stack (mutable)
    str[0] = 'H';          // Works fine
    return 0;
}

Or allocate on the heap:

#include <string.h>
#include <stdlib.h>

int main() {
    char *str = strdup("hello");  // Heap-allocated mutable copy
    if (str == NULL) return 1;
    str[0] = 'H';                 // Works fine
    free(str);
    return 0;
}

In C++, use std::string to avoid this class of bugs entirely.

Fix 10: Check File Permissions and Missing Libraries

Sometimes the segfault has nothing to do with your code. It happens because:

  • A shared library is missing or incompatible. Run ldd ./your_program to check for missing dependencies.
  • A corrupted binary. Recompile from clean source.
  • Insufficient memory. The process can’t allocate the memory it needs. Check with dmesg | tail for OOM messages. If you’re hitting memory limits in containers or services, see Docker OOMKilled or Java OutOfMemoryError for memory-related troubleshooting.

Check dmesg for kernel-level information about the crash:

dmesg | tail -20

You’ll see something like:

[12345.678] program[9876]: segfault at 0 ip 00005555555551a2 sp 00007fffffffde10 error 6 in program[555555555000+1000]

The at 0 tells you it was a null pointer dereference (address 0). The ip value is the instruction pointer — you can look it up with addr2line:

addr2line -e ./your_program 0x00005555555551a2

This prints the source file and line number (if compiled with -g).

Fix 11: System-Level Fixes

Disable ASLR for Debugging

Address Space Layout Randomization (ASLR) makes memory addresses different on each run, which can make debugging harder. Temporarily disable it:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

Warning: Re-enable ASLR after debugging. It is a security feature. Set it back with echo 2 | sudo tee /proc/sys/kernel/randomize_va_space.

Make Core Dumps Persistent

To enable core dumps permanently, not just for the current session:

Add to /etc/security/limits.conf:

*    soft    core    unlimited
*    hard    core    unlimited

Set the core dump pattern:

echo '/tmp/core.%e.%p.%t' | sudo tee /proc/sys/kernel/core_pattern

This writes cores to /tmp/ with the executable name, PID, and timestamp.

If your program runs as a systemd service, note that systemd may manage core dumps separately. See the systemctl service failed guide for debugging services that crash with segfaults.

Check for Hardware Issues

Rarely, segfaults that appear random and irreproducible indicate faulty RAM. Run a memory test:

sudo memtest86+

Or use the kernel’s built-in memory checker by adding memtest to your GRUB boot options.

If you’re seeing segfaults on a server, also check dmesg for MCE (Machine Check Exception) errors, which indicate hardware-level memory corruption.

Still Not Working?

If none of the fixes above resolve your segfault, try these less common approaches:

  • Check for ABI incompatibility. If you’re linking against pre-compiled libraries, make sure they were compiled for the same architecture (x86_64 vs. ARM) and with a compatible compiler version. Mismatched C++ ABIs between GCC versions are a classic source of mysterious segfaults.

  • Look for race conditions. Multi-threaded programs can segfault when two threads access the same data without synchronization. Use ThreadSanitizer (-fsanitize=thread) to detect data races.

  • Check your build system. Partial recompilation after changing struct layouts or function signatures can cause segfaults. Run make clean && make or equivalent to rebuild from scratch.

  • Inspect environment variables. Some programs segfault when expected environment variables are missing or malformed. Check what the program expects. If you’re dealing with shell permission issues that affect how your program runs, see the bash permission denied guide.

  • Update your compiler and OS. Compiler bugs exist, though they’re rare. If you’re on an old GCC (4.x or early 5.x), try upgrading to a modern version. Also update your kernel — segfaults caused by kernel bugs in memory management do happen.

  • Try a different allocator. Replace the default malloc with jemalloc or tcmalloc to see if the crash changes. If it does, the bug likely involves heap corruption. Set it with LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 ./your_program.

  • Run with Electric Fence. libefence replaces malloc with a version that places each allocation at the end of a page. Any buffer overflow triggers an immediate segfault at the exact line, instead of corrupting memory silently:

sudo apt install electric-fence
LD_PRELOAD=/usr/lib/libefence.so ./your_program
  • Binary analysis with objdump. If you don’t have source code, disassemble the binary to understand what instruction caused the fault:
objdump -d ./your_program | less

Search for the instruction address from dmesg output to see the assembly context.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles