Project 1: The First Crash
Learn to intentionally crash a program and capture the evidence - the foundation of all crash analysis.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Beginner |
| Time Estimate | Weekend |
| Language | C |
| Prerequisites | Basic C programming, Linux command line |
| Key Topics | Segmentation faults, ulimit, core_pattern, ELF core dumps |
1. Learning Objectives
By completing this project, you will:
- Understand what causes a segmentation fault at the hardware and OS level
- Write C code that reliably triggers a SIGSEGV signal
- Configure Linux to generate core dump files when processes crash
- Locate core dump files using the kernel’s
core_patternsetting - Verify that a core dump is a valid ELF file ready for analysis
- Create a reusable script to automate crash generation and verification
- Build the foundation for all subsequent crash analysis projects
2. Theoretical Foundation
2.1 Core Concepts
What is a Segmentation Fault?
A segmentation fault (segfault) occurs when a program attempts to access memory that it is not allowed to access. This is a protection mechanism provided by the operating system through the CPU’s Memory Management Unit (MMU).
Virtual Address Space of a Process
┌─────────────────────────────────────────────────────────────┐
│ KERNEL SPACE │
│ (Off limits to user code) │
├─────────────────────────────────────────────────────────────┤
│ STACK │
│ (Local variables, function calls) │
│ ↓ │
│ │
│ Unmapped Memory │
│ (Accessing here causes SIGSEGV) │
│ │
│ ↑ │
│ HEAP │
│ (Dynamically allocated) │
├─────────────────────────────────────────────────────────────┤
│ BSS │
│ (Uninitialized global data) │
├─────────────────────────────────────────────────────────────┤
│ DATA │
│ (Initialized global data) │
├─────────────────────────────────────────────────────────────┤
│ TEXT │
│ (Program code - readonly) │
├─────────────────────────────────────────────────────────────┤
│ Reserved / NULL Page (0x0) │
│ (Deliberately unmapped - catches NULL │
│ pointer dereferences) │
└─────────────────────────────────────────────────────────────┘
The first page of memory (around address 0x0) is intentionally left unmapped. This is a clever design decision: when a programmer forgets to initialize a pointer, it defaults to NULL (address 0). Any attempt to read from or write to this address triggers a segfault immediately, making the bug obvious rather than causing subtle corruption.
The Journey from NULL Dereference to Core Dump
When your program dereferences a NULL pointer, here is the sequence of events:
┌───────────────────────────────────────────────────────────────┐
│ 1. Your Code: *ptr = 42; (where ptr == NULL) │
└─────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 2. CPU attempts to write to virtual address 0x0 │
│ MMU looks up page table entry for address 0x0 │
└─────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 3. Page table shows: Address 0x0 is NOT MAPPED │
│ MMU raises a PAGE FAULT exception │
└─────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 4. CPU transfers control to kernel page fault handler │
│ Kernel sees: fault in user space, invalid address │
│ Decision: This is a protection violation, not fixable │
└─────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 5. Kernel sends SIGSEGV signal to the process │
│ Default action: Terminate and dump core │
└─────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 6. Kernel checks: Is core dumping enabled? │
│ - ulimit -c > 0? │
│ - core_pattern configured? │
│ - Disk space available? │
└─────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 7. If yes: Kernel writes process memory image to disk │
│ Creates ELF core file with all register and memory state │
└─────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────┐
│ 8. Process terminates │
│ Shell displays: "Segmentation fault (core dumped)" │
└───────────────────────────────────────────────────────────────┘
What is a Core Dump?
A core dump is a snapshot of a process’s memory at the moment it crashed. The name comes from early computing when memory was made of ferrite “cores” - tiny magnetic rings that stored bits.
A core dump file contains:
- CPU Register Values: The exact state of all registers (RIP, RSP, RAX, etc.) at the crash moment
- Memory Contents: All mapped memory regions including:
- The program code (text segment)
- Global variables (data/BSS segments)
- The heap (dynamically allocated memory)
- The stack (local variables and call history)
- Memory-mapped files
- Process Metadata: Information about signals, threads, and file descriptors
This file is stored in ELF (Executable and Linkable Format), the same format used for executables and shared libraries on Linux.
The ulimit Command
The ulimit command controls resource limits for processes. The key setting for core dumps is:
ulimit -c [size] # Core file size limit in blocks (512 bytes each)
ulimit -c 0 # Disable core dumps (default on many systems)
ulimit -c unlimited # Allow core dumps of any size
Why is it often disabled by default?
- Core dumps can be large (gigabytes for big applications)
- They may contain sensitive data (passwords, keys in memory)
- They can fill up disk space if many crashes occur
The core_pattern Setting
The kernel’s /proc/sys/kernel/core_pattern controls how core dumps are named and where they are stored:
# Default pattern on many systems - creates file named "core" in current directory
core
# More descriptive pattern with PID
core.%p
# Full path with process name, PID, and timestamp
/var/cores/core.%e.%p.%t
# Piped to a program (used by systemd-coredump, abrt, etc.)
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e
Common pattern specifiers:
%p- PID of the crashed process%e- Executable filename%t- Time of crash (Unix timestamp)%u- User ID%h- Hostname%s- Signal number that caused the dump
2.2 Why This Matters
Understanding crash dump generation is the first step in post-mortem debugging - the art of figuring out what went wrong after a program has already crashed.
In production environments:
- You cannot attach a debugger to a running server
- Crashes happen at 3 AM when no one is watching
- The evidence is gone as soon as the process terminates
Core dumps preserve that evidence. They let you “freeze time” at the moment of failure and examine every detail of the program’s state.
Real-world applications:
- Web servers crash from memory corruption bugs
- Database engines crash from race conditions
- Game engines crash from physics calculation errors
- Operating systems crash (kernel panics) from driver bugs
All of these can be diagnosed if you have a core dump and know how to read it.
2.3 Historical Context
The concept of “dumping core” dates back to the 1950s and 1960s:
- 1949: EDSAC used delay-line memory, not cores
- 1950s: Ferrite core memory became standard (magnetic cores stored bits)
- 1955: First use of “core dump” to describe memory snapshots for debugging
- 1970s: Unix adopted the “core” filename for crash dumps
- 1980s: The term persisted even as core memory was replaced by semiconductor RAM
- 2000s: Linux added
core_patternfor flexible naming - 2010s: systemd-coredump added centralized core management
The name stuck despite the technology change - a testament to how fundamental the concept is.
2.4 Common Misconceptions
Misconception 1: “Core dumps are always created when a program crashes”
Reality: Core dumps are disabled by default on most modern Linux distributions. You must explicitly enable them with ulimit -c unlimited and ensure core_pattern points somewhere writable.
Misconception 2: “Any crash creates a core dump”
Reality: Only certain signals cause core dumps by default:
- SIGSEGV (segmentation fault)
- SIGABRT (abort)
- SIGFPE (floating-point exception)
- SIGBUS (bus error)
- SIGQUIT (quit from keyboard)
- SIGSYS (bad system call)
Signals like SIGKILL and SIGTERM terminate without dumping.
Misconception 3: “Core dumps and crash logs are the same thing”
Reality: Core dumps are binary snapshots of memory. They require special tools (GDB, crash) to interpret. Log files are human-readable text that the application chose to write before crashing.
Misconception 4: “Modern systems don’t use core dumps”
Reality: They absolutely do. Chrome uses Breakpad/Crashpad for minidumps. Linux distributions use systemd-coredump. Production servers use tools like abrt (Red Hat) or apport (Ubuntu). The concept has evolved but remains essential.
3. Project Specification
3.1 What You Will Build
You will create two components:
-
A crashing C program (
crashing_program.c) - A minimal C program that reliably triggers a segmentation fault by dereferencing a NULL pointer. -
An automation script (
run_crash.sh) - A bash script that:- Enables core dumps for the current session
- Compiles the C program with debug symbols
- Runs the program and captures the crash
- Locates the resulting core dump file
- Verifies the core dump is a valid ELF file
3.2 Functional Requirements
- The C program MUST crash with a segmentation fault every time it runs
- The script MUST enable core dumps regardless of the system’s default settings
- The script MUST compile the program with the
-gflag for debug symbols - The script MUST detect whether a core dump was created
- The script MUST use the
filecommand to verify the core dump format - The script MUST handle the case where the core dump is created in a non-obvious location
- The script MUST provide clear, informative output at each step
3.3 Non-Functional Requirements
- Portability: Script should work on major Linux distributions (Ubuntu, Debian, Fedora, CentOS)
- Robustness: Script should handle edge cases (missing compiler, permission issues)
- Educational: Output should explain what is happening at each step
- Cleanup: Script should offer to clean up generated files
3.4 Example Usage / Output
$ ./run_crash.sh
=== Crash Dump Generation Demo ===
[Step 1/6] Checking prerequisites...
- GCC found: gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
- file command found: file-5.41
[Step 2/6] Configuring core dump settings...
- Current ulimit -c: 0 (disabled)
- Setting ulimit -c unlimited...
- New ulimit -c: unlimited
[Step 3/6] Checking core_pattern...
- Current pattern: |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e
- Note: systemd-coredump is handling core dumps
- Core dumps will be stored by coredumpctl
[Step 4/6] Compiling crashing_program.c with debug symbols...
- Command: gcc -g -o crashing_program crashing_program.c
- Compilation successful
[Step 5/6] Running crashing_program...
- Executing ./crashing_program
Segmentation fault (core dumped)
[Step 6/6] Verifying core dump...
- Searching for core dump...
- Found: /var/lib/systemd/coredump/core.crashing_progr.1000.abc123.1703865600000000.zst
- Decompressing for verification...
- File type: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from './crashing_program', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: './crashing_program', platform: 'x86_64'
=== SUCCESS ===
Core dump was generated and verified!
You can now analyze it with:
gdb ./crashing_program /path/to/core
Cleanup: Remove generated files? [y/N]
3.5 Real World Outcome
When this project is complete, you will have:
- A working demonstration of the entire crash-to-dump pipeline
- A reusable script for generating test crashes
- Deep understanding of why crashes sometimes produce dumps and sometimes don’t
- The foundation needed for Project 2 (analyzing the dump with GDB)
4. Solution Architecture
4.1 High-Level Design
┌─────────────────────────────────────────────────────────────────┐
│ run_crash.sh │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────────────────┐ │
│ │ Check │───▶│ Enable │───▶│ Check core_pattern │ │
│ │ prereqs │ │ ulimit │ │ (systemd or file?) │ │
│ └───────────┘ └───────────┘ └───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Compile C program │ │
│ │ gcc -g -o crashing_program crashing_program.c │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Execute program │ │
│ │ ./crashing_program │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ crashing_program.c │ │ │
│ │ │ │ │ │
│ │ │ int main() { │ │ │
│ │ │ int *ptr = NULL; ◄─── Pointer to address 0 │ │ │
│ │ │ *ptr = 42; ◄─── Write to address 0 │ │ │
│ │ │ return 0; causes SIGSEGV │ │ │
│ │ │ } │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Result: SIGSEGV ──▶ Kernel writes core dump │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Locate core dump │ │
│ │ Option A: Look for core.* in current directory │ │
│ │ Option B: Query systemd-coredump via coredumpctl │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Verify with 'file' │ │
│ │ Confirm: "ELF 64-bit LSB core file" │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4.2 Key Components
Component 1: The Crashing Program
A minimal C program that guarantees a segmentation fault. The code must:
- Use a NULL pointer (address 0x0)
- Attempt to write to that address
- Have no error handling that would prevent the crash
Component 2: The Shell Script
A bash script that orchestrates the entire process:
- Prerequisite check: Verify GCC and file are available
- Configuration: Enable core dumps via ulimit
- Detection: Determine how core dumps are handled on this system
- Compilation: Build the C program with debug symbols
- Execution: Run the program and capture exit status
- Verification: Find and validate the core dump file
4.3 Data Structures
The main “data structure” in this project is the ELF core file itself:
ELF Core File Structure
┌──────────────────────────────────────────────────────────────┐
│ ELF Header │
│ - Magic number: 0x7f 'E' 'L' 'F' │
│ - Class: 64-bit (ELFCLASS64) or 32-bit │
│ - Type: ET_CORE (core dump) │
│ - Entry point: Usually 0 (not executable) │
│ - Program header offset │
│ - Section header offset (usually 0 for core dumps) │
└──────────────────────────────────────────────────────────────┘
│ Program Headers │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ PT_NOTE: Contains process metadata │ │
│ │ - NT_PRSTATUS: Register state, signal info │ │
│ │ - NT_PRPSINFO: Process name, state │ │
│ │ - NT_AUXV: Auxiliary vector │ │
│ │ - NT_FILE: Mapped files list │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ PT_LOAD: Loadable memory segment (text) │ │
│ │ - Virtual address, permissions (r-x) │ │
│ │ - File offset to contents │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ PT_LOAD: Loadable memory segment (data) │ │
│ │ - Virtual address, permissions (rw-) │ │
│ │ - File offset to contents │ │
│ └────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ PT_LOAD: Loadable memory segment (stack) │ │
│ │ - Virtual address, permissions (rw-) │ │
│ │ - File offset to contents │ │
│ └────────────────────────────────────────────────────────┘ │
│ ... more segments ... │
└──────────────────────────────────────────────────────────────┘
│ Memory Contents │
│ (The actual bytes from each memory region) │
└──────────────────────────────────────────────────────────────┘
4.4 Algorithm Overview
Step-by-step process:
- Check prerequisites
- Verify
gccis in PATH - Verify
fileis in PATH - Report versions for debugging
- Verify
- Configure core dump settings
- Check current
ulimit -cvalue - Set
ulimit -c unlimited - Verify the change took effect
- Check current
- Determine core dump handling
- Read
/proc/sys/kernel/core_pattern - If starts with
|, it’s piped to a program (e.g., systemd-coredump) - Otherwise, it’s a file path pattern
- Read
- Compile the C program
- Use
gcc -gto include debug symbols - Check for compilation errors
- Use
- Execute and capture crash
- Run
./crashing_program - Save the exit status
- Check for “(core dumped)” message
- Run
- Locate the core dump
- For systemd: use
coredumpctl list - For file patterns: search in current directory or specified path
- Handle compressed dumps (
.zst,.lz4)
- For systemd: use
- Verify the core dump
- Run
filecommand on the dump - Confirm “ELF 64-bit LSB core file” in output
- Run
5. Implementation Guide
5.1 Development Environment Setup
Required tools:
# On Ubuntu/Debian:
sudo apt update
sudo apt install gcc gdb file
# On Fedora/CentOS:
sudo dnf install gcc gdb file
# On Arch Linux:
sudo pacman -S gcc gdb file
# Verify installation:
gcc --version
gdb --version
file --version
Optional but recommended:
# For systemd-coredump systems:
coredumpctl --version
# For manual core dump inspection:
sudo apt install binutils # For readelf
readelf --version
5.2 Project Structure
LEARN_LINUX_CRASH_DUMP_ANALYSIS/
├── README.md
├── P01-the-first-crash.md # This file
├── crashing_program.c # The C program to crash
├── run_crash.sh # The automation script
└── core.* # Generated core dump (gitignored)
5.3 The Core Question You’re Answering
“What are the preconditions that must be satisfied for the Linux kernel to write a core dump file when a process crashes?”
This question drives the entire project. You’ll discover:
- The
ulimit -csetting must be non-zero - The process must have write permission to the core dump location
- Sufficient disk space must be available
- The signal must be one that generates core dumps
- No setuid/setgid bits on the executable (or special handling)
- The
core_patterndetermines where and how dumps are stored
5.4 Concepts You Must Understand First
Before starting, ask yourself:
- What is a pointer in C?
- Can you explain what
int *ptrdeclares? - What is the difference between
ptrand*ptr?
- Can you explain what
- What is NULL?
- What value does NULL typically have? (Answer: 0)
- Why is address 0 special in Linux?
- What is a signal in Unix?
- Can you name a few signals? (SIGTERM, SIGKILL, SIGINT)
- What does “sending a signal” mean?
- What is an executable file?
- How do you compile a C program?
- What does
gcc -o output source.cdo?
If you can answer these, you’re ready to proceed.
5.5 Questions to Guide Your Design
For the C program:
- What is the simplest way to create an invalid memory access?
- Should you use
malloc()and not initialize? Or just use NULL directly? - Should you read from or write to the bad address? Does it matter?
For the script:
- How will you handle systems where
ulimit -c unlimiteddoesn’t persist? - What if the user doesn’t have permission to change ulimit?
- How will you detect if systemd-coredump is active?
- Where might the core dump be if
core_patternis a relative path?
5.6 Thinking Exercise
Before writing any code, trace through this scenario mentally:
- You have a system where
ulimit -cis 0 (disabled) - You run a program that dereferences NULL
- What happens? Walk through each step:
- CPU encounters invalid memory access
- Page fault occurs
- Kernel decides to send SIGSEGV
- Kernel checks if it should dump core
- With
ulimit -cat 0, what happens?
Now trace the same scenario with ulimit -c unlimited:
- At which step does the behavior differ?
- What additional work does the kernel do?
5.7 Hints in Layers
Hint 1: Starting Point (Conceptual)
The crashing program is literally 5 lines of code. You need:
- A pointer variable
- The pointer set to an invalid address
- An attempt to use that pointer
The script needs to manipulate shell settings and find files. Start by doing each step manually in the terminal before scripting it.
Hint 2: Next Level (More Specific)
For the C program, remember:
NULLis defined in<stddef.h>(or just use0)- The simplest crash:
*p = value;where p is NULL
For the script:
ulimit -cis a shell builtin - it only affects the current shell and childrencore_patternis read withcat /proc/sys/kernel/core_pattern- A pattern starting with
|means piped to a program
Hint 3: Technical Details (Approach)
C program structure:
// What header(s) do you need? (Think about it)
// Hint: Maybe none for the minimal version!
int main(void) {
// Declare a pointer to int
// Set it to NULL
// Dereference and assign a value
// Return (you'll never reach this)
}
Script structure:
#!/bin/bash
# Check for gcc and file commands
# Set ulimit -c unlimited
# Check core_pattern
# Compile with gcc -g
# Run the program
# Find the core file
# Verify with file command
Hint 4: Tools/Debugging (Verification)
How to verify each step manually:
# Check current ulimit
ulimit -c
# Set unlimited
ulimit -c unlimited
# Verify it changed
ulimit -c
# Check core pattern
cat /proc/sys/kernel/core_pattern
# If using systemd, check for recent dumps
coredumpctl list
# Compile manually
gcc -g -o crashing_program crashing_program.c
# Run and observe
./crashing_program
echo $? # Exit code (139 = 128 + 11, where 11 = SIGSEGV)
# Find core (traditional systems)
ls -la core*
# Verify with file
file core.1234
5.8 The Interview Questions They’ll Ask
- “What is a segmentation fault and what causes it?”
- Expected: Memory access violation, typically NULL pointer dereference, use after free, or buffer overflow
- “Why might a core dump not be generated when a program crashes?”
- Expected: ulimit set to 0, insufficient disk space, pipe to handler that drops it, setuid binary restrictions
- “What information is stored in a core dump file?”
- Expected: CPU register state, process memory (stack, heap, data segments), signal information, thread info
- “How would you enable core dumps on a production server?”
- Expected: Set ulimit in service config, configure core_pattern, ensure disk space, consider security implications
- “What’s the difference between SIGSEGV and SIGBUS?”
- Expected: SIGSEGV = unmapped memory access; SIGBUS = aligned memory access violation or bad physical address
- “If you see ‘Segmentation fault’ without ‘(core dumped)’, what do you check?”
- Expected: ulimit -c value, core_pattern setting, disk space, directory permissions
5.9 Books That Will Help
| Topic | Book | Chapter(s) |
|---|---|---|
| Virtual Memory and Signals | Computer Systems: A Programmer’s Perspective (Bryant & O’Hallaron) | Chapter 9 |
| C Pointers | The C Programming Language (Kernighan & Ritchie) | Chapter 5 |
| Unix Signals | The Linux Programming Interface (Kerrisk) | Chapters 20-22 |
| Core Dumps | Advanced Programming in the UNIX Environment (Stevens) | Chapter 10 |
| Shell Scripting | The Linux Command Line (Shotts) | Chapters 24-36 |
5.10 Implementation Phases
Phase 1: Manual Testing (30 minutes)
- Perform every step manually in the terminal
- Write a crashing C program in a text editor
- Compile and run it
- Find and verify the core dump
- Document what you did
Phase 2: Basic Script (1 hour)
- Create a script that does the manual steps
- Add echo statements to show progress
- Test on your system
Phase 3: Robust Script (2-3 hours)
- Add prerequisite checking
- Handle systemd-coredump vs. traditional core files
- Add error handling
- Test on multiple configurations
Phase 4: Polish (1 hour)
- Add cleanup functionality
- Add colored output (optional)
- Add help message
- Final testing
5.11 Key Implementation Decisions
Decision 1: How to crash?
Option A: NULL pointer dereference (simplest)
int *p = NULL;
*p = 42;
Option B: Array out of bounds
int arr[10];
arr[1000000] = 42; // May or may not crash depending on system
Option C: Call abort()
#include <stdlib.h>
abort(); // Sends SIGABRT, not SIGSEGV
Recommendation: Use Option A. It’s the most reliable and demonstrates the most common cause of crashes.
Decision 2: How to handle systemd-coredump?
Option A: Ignore systemd, write your own core_pattern
- Requires root
- Modifies system configuration
Option B: Use coredumpctl to retrieve dumps
- Works with the system as configured
- More realistic for real-world debugging
Recommendation: Option B for portability, but document how to do Option A.
Decision 3: Should the script require root?
Option A: Require root, modify core_pattern Option B: Run as regular user, work with existing configuration
Recommendation: Option B. The script should demonstrate the concepts without requiring system modification.
6. Testing Strategy
Test Case 1: Verify Crash Behavior
# Compile without the script
gcc -g -o crashing_program crashing_program.c
# Run the program
./crashing_program
# Expected output:
# Segmentation fault (core dumped)
# Verify exit code
echo $?
# Expected: 139 (128 + 11, where 11 = SIGSEGV)
Test Case 2: Verify Core Dump Generation
# Enable core dumps
ulimit -c unlimited
# Run the program
./crashing_program
# Check for core file
ls -la core* 2>/dev/null || coredumpctl list | head -5
# At least one method should show a dump
Test Case 3: Verify ELF Format
# Get the core file path (varies by system)
CORE_FILE=$(ls core* 2>/dev/null | head -1)
# Or for systemd:
# coredumpctl dump -o /tmp/core
file $CORE_FILE
# Expected output contains:
# ELF 64-bit LSB core file
Test Case 4: Test Script Error Handling
# Test with missing gcc
PATH=/bin ./run_crash.sh
# Should report: GCC not found
# Test with insufficient permissions
chmod 000 crashing_program.c
./run_crash.sh
# Should report: Cannot read source file
7. Common Pitfalls & Debugging
Pitfall 1: “Segmentation fault” but No “(core dumped)”
Symptom: The program crashes but no core file appears.
Root Cause: ulimit -c is set to 0.
Fix:
# Check the limit
ulimit -c
# If it shows 0:
ulimit -c unlimited
Verification: Run the crashing program again and check for “(core dumped)” in the output.
Pitfall 2: Can’t Find the Core File
Symptom: “(core dumped)” appears but no file in current directory.
Root Cause: core_pattern is set to a different location or piped to systemd-coredump.
Fix:
# Check where cores go
cat /proc/sys/kernel/core_pattern
# If it starts with |, use coredumpctl:
coredumpctl list
coredumpctl dump -o ./core # Extract latest dump
Verification: The coredumpctl list command shows your crash.
Pitfall 3: Compilation Errors
Symptom: gcc: command not found or “undefined reference”
Root Cause: GCC not installed or wrong package.
Fix:
# Ubuntu/Debian
sudo apt install build-essential
# Fedora
sudo dnf install gcc
# Arch
sudo pacman -S gcc
Verification: gcc --version shows version information.
Pitfall 4: Core Dump File is Empty or Truncated
Symptom: Core file exists but is 0 bytes or very small.
Root Cause: Insufficient disk space or ulimit too small.
Fix:
# Check disk space
df -h .
# Check ulimit (might be limited to specific size)
ulimit -c
# Set truly unlimited
ulimit -c unlimited
Verification: ls -la core* shows a file of several kilobytes or more.
Pitfall 5: Permission Denied When Writing Core
Symptom: Shell might not show “(core dumped)” if write fails.
Root Cause: Current directory is not writable, or core_pattern points to unwritable location.
Fix:
# Check directory permissions
ls -la .
# Create cores in /tmp if needed
sudo sysctl kernel.core_pattern=/tmp/core.%e.%p
Verification: Run in a directory where you have write permission.
Pitfall 6: setuid/setgid Binary Restrictions
Symptom: Core dump not generated for setuid programs.
Root Cause: Security measure to prevent sensitive data leakage.
Fix: Don’t use setuid for testing, or enable with:
# Allow core dumps for setuid (not recommended in production)
sudo sysctl fs.suid_dumpable=1
Verification: Use a regular (non-setuid) binary for this project.
8. Extensions & Challenges
Extension 1: Different Crash Types
Modify your crashing program to demonstrate different signals:
// SIGABRT (signal 6)
#include <stdlib.h>
abort();
// SIGFPE (signal 8) - Floating point exception
int x = 1 / 0; // Note: Modern compilers may optimize this
// SIGBUS (signal 7) - Requires aligned access violation
// More complex to trigger reliably
Create separate test programs for each and compare the core dumps.
Extension 2: Stack Trace Preview
Extend your script to show a basic stack trace:
# After finding the core dump, add:
echo "Quick backtrace preview:"
gdb -batch -ex "bt" ./crashing_program ./core 2>/dev/null
Extension 3: Core Dump Comparison Tool
Write a script that:
- Generates multiple crashes
- Compares their core dump sizes
- Extracts and compares backtraces
- Reports differences (useful for detecting heap size variations)
Extension 4: Custom Core Pattern
Configure your own core pattern on a test VM:
# As root:
echo "/var/cores/core.%e.%p.%t" > /proc/sys/kernel/core_pattern
mkdir -p /var/cores
chmod 777 /var/cores # For testing only!
# Test and observe the naming
./crashing_program
ls /var/cores/
Extension 5: Understand the ELF Structure
Use readelf to explore the core dump:
readelf -h core # Show ELF header
readelf -l core # Show program headers
readelf -n core # Show notes (process info)
Write a summary of what you find.
9. Real-World Connections
Production Core Dump Management
In production environments, organizations use:
1. systemd-coredump (Linux)
- Compresses dumps automatically
- Stores in
/var/lib/systemd/coredump/ - Query with
coredumpctl - Can be configured to send to remote servers
2. abrt (Red Hat/Fedora)
- Automatic Bug Reporting Tool
- Analyzes crashes, creates reports
- Can submit to bugzilla automatically
3. apport (Ubuntu)
- Creates .crash files in /var/crash
- Integrates with Ubuntu’s error tracker
- Can be configured for custom handling
4. Google Breakpad/Crashpad
- Cross-platform (Windows, macOS, Linux)
- Generates minidumps (smaller than full cores)
- Used by Chrome, Firefox, Electron apps
Example: How Chrome Handles Crashes
Chrome Process
│
▼ (crash)
┌─────────────────────────────────────────┐
│ Crashpad Handler │
│ - Catches the crash signal │
│ - Creates minidump in memory │
│ - Writes to /tmp/crash-reports/ │
│ - Uploads to crash.corp.google.com │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Google's Crash Analysis Server │
│ - Symbolizes the stack trace │
│ - Groups similar crashes │
│ - Creates bug reports automatically │
└─────────────────────────────────────────┘
Security Considerations
Core dumps can contain:
- Passwords in memory
- Encryption keys
- Personal user data
- API tokens and secrets
Production systems often:
- Disable core dumps entirely
- Encrypt core dumps at rest
- Restrict access to core dump directories
- Automatically delete dumps after analysis
- Redact sensitive data before upload
10. Resources
Official Documentation
man core- Core dump file format and configurationman signal- Signal types and default actionsman ulimit- Shell resource limitsman gdb- GNU Debugger manual
Online References
Tools
- GDB - GNU Debugger for analyzing core dumps
- readelf - Display ELF file information
- file - Identify file types
- coredumpctl - Query systemd-coredump
- objdump - Display object file information
Related Projects in This Series
- Project 2: The GDB Backtrace - Use GDB to analyze the core dump you created
- Project 3: The Memory Inspector - Examine memory contents in a core dump
- Project 4: The Automated Crash Detective - Build a script to automate analysis
11. Self-Assessment Checklist
Before considering this project complete, verify:
Conceptual Understanding
- I can explain what a segmentation fault is
- I understand why address 0 causes a crash
- I can describe what information is in a core dump
- I know the difference between SIGSEGV, SIGABRT, and SIGBUS
- I understand why core dumps might be disabled by default
Practical Skills
- I can write C code that crashes with a segfault
- I can enable core dumps with ulimit
- I can find the core_pattern setting
- I can locate a core dump file on my system
- I can verify a core dump is a valid ELF file
Debugging Ability
- I know what to check when “(core dumped)” doesn’t appear
- I can troubleshoot missing core files
- I understand the difference between systemd and traditional core handling
Script Quality
- My script checks prerequisites before running
- My script provides informative output
- My script handles errors gracefully
- My script works on at least one Linux distribution
12. Submission / Completion Criteria
This project is complete when:
- The C program (
crashing_program.c) exists and:- Compiles without warnings with
gcc -Wall -g - Crashes with a segmentation fault 100% of the time
- Is minimal (under 10 lines of code)
- Compiles without warnings with
- The shell script (
run_crash.sh) exists and:- Runs without errors on your system
- Enables core dumps
- Compiles the C program
- Runs the program and captures the crash
- Locates the core dump file
- Verifies the core dump with the
filecommand - Provides clear output at each step
- You can answer:
- Why does dereferencing NULL crash the program?
- What does
ulimit -c unlimiteddo? - Where does your system store core dumps?
- How do you verify a file is a valid core dump?
- You have documented:
- Any system-specific configuration needed
- How to run your script
- What the expected output looks like
Next Steps
Once you have completed this project, you are ready for Project 2: The GDB Backtrace, where you will:
- Load the core dump you created into GDB
- Learn to read a stack backtrace
- Understand the importance of debug symbols
- Find the exact line of code that caused the crash
The core dump you generated here is your ticket to learning the most fundamental skill in crash analysis: reading a backtrace.
Project 1 of 10 in the Linux Crash Dump Analysis learning path.