AI is redefining software quality and security. Insights from 450 CISOs & devs →
Aikido

Prevent segmentation faults: memory safety in C and C++

Rule
Prevent common segmentation fault patterns.
Null pointer dereferences, buffer overflows, and use-after-free errors cause crashes and security vulnerabilities.
Supported languages: C/C++

Introduction

Segmentation faults remain the most common source of crashes and exploitable vulnerabilities in C/C++ applications. These memory access violations occur when code attempts to read or write memory it doesn't own, typically through null pointer dereferences, buffer overflows, dangling pointers, or accessing freed memory. A single segfault can bring down production servers, but worse, many segfault patterns are exploitable for arbitrary code execution.

Why it matters

Security implications: Buffer overflows and use-after-free vulnerabilities are the foundation of most memory corruption exploits. Attackers leverage these to overwrite return addresses, inject shellcode, or manipulate program control flow. The 2014 Heartbleed vulnerability was a buffer over-read. Modern exploits still target these patterns because they provide direct memory access to attackers.

System stability: Segmentation faults crash your application immediately with no graceful degradation. In production systems, this means dropped requests, interrupted transactions, and corrupted state. Unlike higher-level language exceptions that can be caught, segfaults terminate the process, requiring restart and recovery procedures.Attack surface expansion: Every unchecked pointer dereference, strcpy, memcpy, or array access without bounds checking is a potential entry point for exploitation. Attackers chain these vulnerabilities together, using one to corrupt memory in ways that enable exploiting another.

Code examples

❌ Non-compliant:

void process_user_data(const char* input) {
    char buffer[64];
    strcpy(buffer, input);  // No bounds checking

    char* token = strtok(buffer, ",");
    while (token != NULL) {
        process_token(token);
        token = strtok(NULL, ",");
    }
}

int* get_config_value(int key) {
    int* value = (int*)malloc(sizeof(int));
    *value = lookup_config(key);
    return value;  // Caller must free, but no documentation
}

Why it's unsafe: The strcpy() call causes buffer overflow if input exceeds 63 bytes, allowing attackers to overwrite stack memory. The get_config_value() function leaks memory on every call and creates dangling pointer risk if callers free the memory while other code still references it.

✅ Compliant:

void process_user_data(const char* input) {
    if (!input) return;

    size_t input_len = strlen(input);
    char* buffer = malloc(input_len + 1);
    if (!buffer) return;

    strncpy(buffer, input, input_len);
    buffer[input_len] = '\0';

    char* token = strtok(buffer, ",");
    while (token != NULL) {
        process_token(token);
        token = strtok(NULL, ",");
    }

    free(buffer);
}

int get_config_value(int key, int* out_value) {
    if (!out_value) return -1;

    *out_value = lookup_config(key);
    return 0;  // Caller owns out_value memory
}

Why it's safe: Null pointer checks prevent dereference crashes. Dynamic allocation eliminates fixed buffer size limits. Bounds-checked copying with strncpy() prevents overflow. Clear ownership semantics in get_config_value() where caller provides memory avoid allocation confusion and leaks.

Conclusion

Memory safety in C and C++ requires defensive programming at every pointer operation and memory allocation. Segmentation faults aren't inevitable, they're preventable through consistent null checks, bounds validation, and clear memory ownership patterns. Catching these patterns before production prevents both crashes and exploitable vulnerabilities.

FAQs

Got Questions?

What are the most common segmentation fault patterns?

The top five are: null pointer dereferences (accessing ptr->field without checking ptr != NULL), buffer overflows (strcpy, sprintf, array access without bounds checks), use-after-free (accessing memory after free()), double-free (calling free() twice on same pointer), and stack buffer overflow (writing past local array bounds). These account for over 80% of memory corruption vulnerabilities in C/C++ codebases.

How do I prevent the most common segfault patterns?

Always check pointers before dereferencing (if (!ptr) return;). Use length-limited functions: strncpy() instead of strcpy(), snprintf() instead of sprintf(). Track buffer sizes explicitly and validate before writes. In C++, prefer std::string and std::vector which handle bounds automatically. Initialize all pointers to NULL and set them to NULL after freeing.

What's the difference between undefined behavior and segmentation faults?

Segmentation faults are one possible outcome of undefined behavior, but not the only one. Undefined behavior might corrupt memory silently, produce wrong results, or appear to work fine in testing but fail in production. Segfaults are "lucky" because they crash immediately and visibly. Silent memory corruption is worse because it goes undetected while corrupting application state or creating security vulnerabilities.

What's the performance cost of null pointer checks?

Minimal. A null pointer check is a single comparison instruction that modern CPUs execute in nanoseconds. The branch prediction is usually accurate since most pointers are valid. The performance cost is negligible compared to the cost of a production crash or security vulnerability. Profile before optimizing, and you'll find null checks rarely appear in hot paths.

Get secure for free

Secure your code, cloud, and runtime in one central system.
Find and fix vulnerabilities fast automatically.

No credit card required | Scan results in 32secs.