Back to Blog
critical SEVERITY9 min read

Critical Use-After-Free: The Dangerous krealloc() Pattern in Linux Kernel Code

A critical memory safety vulnerability was discovered and fixed in the Linux kernel's SSDFS filesystem driver, where directly assigning the return value of krealloc() to the original pointer could cause use-after-free conditions or NULL pointer dereferences when memory allocation fails. This well-known dangerous pattern, explicitly warned against in Linux kernel coding guidelines, could allow attackers to trigger memory corruption under low-memory conditions. The fix implements the safe temporar

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published May 25, 2026•Reviewed June 3, 2026

Answer Summary

This is a use-after-free vulnerability (CWE-416) in C kernel code caused by the unsafe krealloc() pattern where the return value is directly assigned to the original pointer. When krealloc() fails and returns NULL, the original pointer is overwritten, creating a use-after-free condition. The fix uses a temporary pointer to safely handle allocation failures, preserving the original pointer and preventing memory corruption.

Vulnerability at a Glance

cweCWE-416 (Use After Free), CWE-690 (Unchecked Return Value to NULL Pointer Dereference)
fixUse temporary pointer to safely handle allocation failures
riskMemory corruption, kernel panic, potential privilege escalation under low-memory conditions
languageC (Linux Kernel)
root causeDirect assignment of krealloc() return value to original pointer without NULL checking
vulnerabilityUse-After-Free via Unsafe krealloc() Pattern

Introduction

Memory management bugs are among the most dangerous vulnerability classes in systems programming. They're subtle, they're hard to spot in code review, and when exploited, they can lead to privilege escalation, kernel panics, or arbitrary code execution. Today we're examining a critical vulnerability discovered and patched in fs/ssdfs/peb_init.c — the Physical Erase Block (PEB) initialization code in the SSDFS (Segmented SSD File System) driver in the Linux kernel.

The vulnerability falls into a category that the Linux kernel community has documented as a known dangerous anti-pattern: directly assigning the return value of krealloc() to the pointer being reallocated. It sounds almost mundane. It's anything but.

If you write C code — especially kernel or systems-level C — understanding this bug pattern could save you from introducing a critical security vulnerability into production systems.


The Vulnerability Explained

What is krealloc()?

krealloc() is the kernel's memory reallocation function, analogous to realloc() in userspace. It attempts to resize a previously allocated memory block. Its signature looks like this:

void *krealloc(const void *p, size_t new_size, gfp_t flags);

Here's the critical behavior you must internalize: if krealloc() fails, it returns NULL — but it has already freed the original memory block internally.

This is not a bug in krealloc(). This is documented, expected behavior. But it creates a deadly trap for the unwary developer.

The Vulnerable Pattern

The vulnerable code in fs/ssdfs/peb_init.c at line 1289 looked something like this:

/* VULNERABLE: Direct assignment of krealloc() return value */
buf->ptr = krealloc(buf->ptr, new_size, GFP_KERNEL);
if (!buf->ptr) {
    /* Too late! The original memory is already gone.
       buf->ptr is now NULL, and the original pointer
       is lost forever. */
    return -ENOMEM;
}

This single line creates two distinct but related vulnerabilities:

Vulnerability 1: Use-After-Free

When krealloc() fails and returns NULL, the kernel has already freed the original memory block that buf->ptr pointed to. If any other part of the code holds a reference to that old pointer value (perhaps cached in a local variable, another struct field, or a linked data structure), that reference now points to freed memory.

Any subsequent read or write through that stale pointer is a use-after-free — one of the most exploitable memory safety bugs in existence.

Vulnerability 2: NULL Pointer Dereference

Even without a secondary reference, the code has now lost its only handle to the buffer. buf->ptr is NULL. Any subsequent code that accesses buf->ptr without checking for NULL will trigger a NULL pointer dereference, causing a kernel panic (oops) at best, and at worst, providing a primitive for privilege escalation on systems with mmap_min_addr misconfigurations.

Why Is This Particularly Dangerous in Kernel Code?

In userspace, a NULL dereference typically just crashes your process. In kernel space, the stakes are dramatically higher:

  • Kernel panics can bring down entire systems or virtual machines
  • Memory corruption in kernel space can overwrite security-critical data structures
  • Privilege escalation becomes possible if an attacker can control what gets written to the freed/NULL memory region
  • Filesystem corruption is a real risk in storage driver code — the SSDFS driver manages how data is physically laid out on SSDs

Attack Scenario

Consider a scenario where an adversarial process or a crafted filesystem image triggers repeated memory allocations in the SSDFS driver under memory pressure:

  1. Attacker triggers memory pressure — through memory exhaustion, crafted filesystem operations, or exploiting another allocation path
  2. krealloc() fails — returns NULL for a large reallocation request
  3. buf->ptr is overwritten with NULL — the original buffer reference is lost
  4. A stale reference remains — another code path still holds the old pointer value
  5. Use-after-free occurs — the freed memory is reallocated for another purpose, and the stale pointer now reads/writes attacker-influenced data
  6. Kernel memory corruption — depending on what was reallocated into that memory, the attacker may be able to manipulate kernel data structures

This class of vulnerability is referenced as CWE-416: Use After Free and has been the root cause of numerous high-severity CVEs in the Linux kernel over the years.


The Fix

The fix implements the well-established safe temporary pointer pattern — a simple, robust solution that every C developer working with dynamic memory should internalize.

Before (Vulnerable)

/* DANGEROUS: Direct assignment */
buf->ptr = krealloc(buf->ptr, new_size, GFP_KERNEL);
if (!buf->ptr) {
    /* Original pointer is already freed and lost!
       We have a potential use-after-free and
       definite NULL pointer dereference risk. */
    return -ENOMEM;
}
buf->size = new_size;

After (Fixed)

/* SAFE: Use a temporary pointer */
void *tmp;

tmp = krealloc(buf->ptr, new_size, GFP_KERNEL);
if (!tmp) {
    /* krealloc failed. buf->ptr still points to the
       original, valid memory. We can safely return
       an error and the caller can handle cleanup
       properly with a valid pointer. */
    return -ENOMEM;
}

/* Only update buf->ptr after confirming success */
buf->ptr = tmp;
buf->size = new_size;

Why This Works

The fix introduces a temporary pointer tmp that receives the return value of krealloc(). This creates a critical separation:

  1. buf->ptr is not touched until we know the allocation succeeded — the original pointer remains valid throughout the error-checking phase
  2. On failure, buf->ptr still points to the original valid memory. The caller receives an error code and can make an informed decision about how to handle the failure — including properly freeing the original buffer if needed
  3. On success, we atomically update buf->ptr to the new allocation only after confirming it's non-NULL
  4. No stale references — because buf->ptr was never overwritten with NULL, any code holding a reference to buf->ptr's value still has a valid pointer during the error handling window

This pattern is explicitly recommended in the Linux kernel coding style documentation and is enforced by static analysis tools like the kernel's own sparse checker and Coccinelle semantic patches.

The Regression Test

The fix includes a comprehensive regression test suite that validates the security invariant across a wide range of inputs, including edge cases like:

@pytest.mark.parametrize("payload", [
    (4096, 2**63 - 1, True, "overflow_size"),    # Huge allocation that must fail
    (4096, -1, True, "negative_size"),             # Negative size
    (4096, 2**32, True, "huge_allocation"),        # 4GB allocation
    (0, 4096, False, "null_to_valid"),             # Growing from null
    (4096, 8192, False, "normal_grow"),            # Normal growth
    # ... and many more
])
def test_realloc_security_invariant(payload):
    """
    INVARIANT: If original ptr was valid and realloc failed,
    buf.ptr must still point to the original valid memory.
    """

The key invariant being tested:

# CRITICAL INVARIANT: On failure, original ptr must be preserved
if had_valid_ptr and not success:
    assert buf.ptr is not None, (
        "SECURITY VIOLATION: buf.ptr was set to None after failed realloc. "
        "Original valid pointer lost — potential use-after-free or NULL dereference."
    )
    assert buf.ptr is original_ptr, (
        "SECURITY VIOLATION: buf.ptr changed after failed realloc. "
        "Original pointer must be preserved to prevent memory corruption."
    )

Conclusion

The krealloc() use-after-free pattern is a perfect example of how a single line of seemingly reasonable code can introduce a critical security vulnerability. The fix is simple — a temporary pointer and a null check — but the consequences of getting it wrong range from kernel panics to potential privilege escalation.

Key takeaways from this vulnerability:

  1. Never directly assign realloc()/krealloc() return values to the source pointer — always use a temporary
  2. Memory allocation failures are not just error conditions — they're potential security boundaries that must be handled correctly
  3. The Linux kernel coding guidelines exist for good reason — they encode decades of hard-won knowledge about exactly these kinds of bugs
  4. Static analysis and runtime sanitizers are not optional — tools like KASAN and Coccinelle exist precisely to catch patterns like this before they reach production
  5. Regression tests for security invariants are valuable — the test suite included with this fix ensures this exact bug pattern cannot silently reappear

The fact that this pattern is explicitly documented as dangerous in the kernel guidelines, yet still appears in code, is a reminder that security education, tooling, and process must work together. No single defense is sufficient.

Write safe code, use your tools, and always check that return value before you assign it.


This vulnerability was identified and fixed as part of automated security scanning by OrbisAI Security. The fix has been verified with build validation, scanner re-scan, and LLM-assisted code review.

Prevention and further reading

Frequently Asked Questions

Is just adding NULL checks enough?

No. NULL checks alone don't solve this—you must use a temporary pointer to avoid overwriting the original pointer reference when allocation fails.

Related Articles

critical

How buffer overflow via strcpy() happens in C Kconfig parsing and how to fix it

A critical buffer overflow vulnerability was discovered in the Linux kernel's Kconfig build system where `strcpy()` copied user-controlled symbol values into a fixed-size buffer without bounds checking. This flaw in `scripts/kconfig/symbol.c` could allow attackers to overwrite adjacent memory when processing malicious Kconfig files. The fix replaces the unsafe `strcpy()` with `memcpy()` using explicit length calculations.

medium

How buffer overflow happens in C kernel driver (qcom_usbnet_main.c) and how to fix it

A series of unsafe `sprintf()` calls in the Qualcomm USB network kernel driver (`qcom_usbnet_main.c`) created buffer overflow conditions that, when combined with other memory corruption primitives in the same file, could allow an attacker with physical USB access to escalate privileges to root. The fix replaces unbounded `sprintf()` and `snprintf()` misuse with properly bounded `snprintf()` and `scnprintf()` calls that respect actual buffer sizes. This is a textbook example of how a seemingly mi

critical

How heap buffer overflow happens in C kconfig symbol.c and how to fix it

A heap buffer overflow vulnerability was discovered in `scripts/kconfig/symbol.c`, where `strcpy()` was used to copy a configuration symbol value into a heap-allocated buffer without verifying that the source string fit within the allocated size. This CWE-120 flaw could allow an attacker or malformed build configuration to corrupt heap memory, potentially leading to arbitrary code execution during the kernel build process. The fix replaces `strcpy()` with a bounds-aware `memcpy()` and replaces u

high

GPIO Bounds Checking: Fixing an Out-of-Bounds Access in py32ioexp Driver

A high-severity out-of-bounds access vulnerability was discovered and patched in the `py32ioexp` Linux GPIO expander driver. The `py32io_gpio_direction_input()` function failed to validate a user-supplied pin offset against the chip's declared GPIO count, opening the door to memory corruption via the GPIO character device interface. A two-line bounds check now closes the vulnerability cleanly and efficiently.

critical

Critical Kernel Buffer Overflow Fixed in BPF x86 Native Lab Module

A critical buffer overflow vulnerability (CWE-120) was discovered and patched in `module/x86/bpf_x86_native_lab.c`, where a bounds check on BPF blob length was only performed inside an `emit` conditional branch — leaving a window for kernel memory corruption when `emit` was false. The fix relocates the length validation before any branching logic, ensuring no code path can proceed with an oversized blob. This type of kernel-level vulnerability is particularly dangerous because successful exploit

critical

CVE-2026-59873: node-tar 7.5.11 DoS via Crafted Gzip Bomb

node-tar versions 7.5.11 through 7.5.18 are vulnerable to a denial-of-service attack through maliciously crafted gzip archives that decompress to disproportionately large sizes. An attacker can exploit this to exhaust memory and CPU resources by submitting a small, highly compressed archive that expands beyond configured limits during extraction.