In June 2024, the Linux kernel maintainers resolved a serious memory corruption vulnerability in the Loongson2 clock driver, tracked as CVE-2024-53193. This flaw could potentially lead to unpredictable system behavior, kernel panics, or other security issues if exploited by an attacker with the ability to manipulate clock hardware structures—usually via a vulnerable kernel module or faulty driver code.

This long read will break down what went wrong, how it was detected, the exact code involved, and the details of the security fix. Unlike dense technical advisories, this write-up aims to guide everyone—including beginner kernel developers and curious sysadmins—through the bug.

What Are Flexible Array Members?

In modern C, a structure can end with an array member declared as type name[];. This is called a *flexible array member*, and it's useful when allocating structs whose last member is variable-length. You must always place these at the very end of your struct—because that's where extra memory will get laid out.

Where Did It Go Wrong?

In the Linux kernel's Loongson2 clock driver code (drivers/clk/clk-loongson2.c), a composite structure was defined like so:

struct loongson2_clk_provider {
    void __iomem *base;
    struct device *dev;
    struct clk_hw_onecell_data clk_data;
    spinlock_t clk_lock;   /* protect access to DIV registers */
};

But the flexible array is not at the end—it's inside clk_data, placed before clk_lock. And here's how memory gets allocated:

clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num), GFP_KERNEL);

Later, values are written into clp->clk_data.hws[p->id] = hw;. If hw pointers are written beyond the space allocated for clk_data.hws, it will overwrite into clk_lock (or other variables), causing memory corruption.

Unpredictable kernel memory corruption: Anything from crashes to subtle misbehavior.

- Security risk: An attacker or buggy code could (theoretically) leverage this to tamper with kernel memory.

- GCC's warnings will mention [-Wflex-array-member-not-at-end], e.g.

drivers/clk/clk-loongson2.c:32:36: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]

- Static analysis may point to out-of-bounds writes in the clock provider allocation or initialization.

The Fix

The solution is to move the flexible array containing structure (struct clk_hw_onecell_data) to the very end of struct loongson2_clk_provider so no other members come after it.

Before (buggy)

struct loongson2_clk_provider {
    void __iomem *base;
    struct device *dev;
    struct clk_hw_onecell_data clk_data; // <== OOPS! Not at the end
    spinlock_t clk_lock;   // <== will be *overwritten*
};

After (fixed)

struct loongson2_clk_provider {
    void __iomem *base;
    struct device *dev;
    spinlock_t clk_lock;   /* protect access to DIV registers */
    struct clk_hw_onecell_data clk_data; /* MUST BE LAST! */
    /* clk_data must be last: it has a flexible array member (hws[]) */
};

A comment is added to prevent future mistakes.

Commit message:
view the upstream patch

How Could This Be Exploited?

If an attacker triggered calls assigning clp->clk_data.hws[p->id] with out-of-bounds or large index values, the assignment would overwrite memory past clk_data. Because clk_lock (a spinlock) comes immediately after, this could corrupt the lock, leading to unpredictable synchronization behavior, denial of service, or—if further leveraged—possibly privilege escalation (though this is complex in practice).

Suppose a kernel module or user with device-level privileges could control the value of p->id

// Suppose ids go up to 10, but...
clp->clk_data.hws[100] = some_pointer; 
// Now you've trashed memory after clk_data, including clk_lock and whatever else

Heap Corruption

Because the memory for clp is heap-allocated, repeated overflows can corrupt adjacent heap metadata, making more advanced exploitation possible with additional info-leak or arbitrary write bugs.

Minimal Proof-of-Concept

Because this is a kernel-space bug, normal users can't directly exploit this without ring- access, but here's a simplified version of how you could see the bug in userspace C:

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

struct onecell_data {
    int nclocks;
    int ids[];
};

struct provider {
    int a;
    struct onecell_data data;
    int b; // Should not come after data!
};

int main() {
    struct provider *p;
    int n = 2;
    p = malloc(sizeof(struct provider) + n * sizeof(int));
    p->data.ids[2] = 12345; // Write out of bounds (over b)
    printf("b = %d\n", p->b); // Likely prints 12345
}

Original Patch Commit:

clk: clk-loongson2: Fix memory corruption bug in struct loongson2_clk_provider

GCC Documentation:

Flexible Array Members

CVE page:

CVE-2024-53193 at cve.org (coming soon)

Summary

CVE-2024-53193 highlights the dangers of misusing C flexible array members in kernel code. The bug in the Loongson2 clock driver allowed crucial kernel spinlocks to be overwritten through simple out-of-bounds writes, putting system stability and security at risk. This was caught by smart compiler warnings and fixed by rearranging structure members and clarifying comments, improving safety for all future kernel releases.

Pay attention to compiler warnings about flexible arrays.

- Review memory allocation sizes carefully in code that uses flexible or variable-sized structures.

If you want to learn more, check out

- Original kernel commit
- Linux kernel security mailing list

Timeline

Published on: 12/27/2024 14:15:26 UTC
Last modified on: 05/04/2025 09:55:25 UTC