CVE-2023-52601 - Array Index Out of Bounds in JFS Linux Kernel Filesystem – Explained and Exploited

On modern Linux systems, the filesystem code is critical for data stability and system security. Recently, a vulnerability was discovered and patched in the JFS (Journaled File System) implementation of the Linux kernel. This flaw, labeled CVE-2023-52601, allowed for an array-index-out-of-bounds error within the dbAdjTree function – a bug that, if left unpatched, could enable attackers to crash the kernel or potentially execute arbitrary code.

In this article, we’ll break down what caused CVE-2023-52601, illustrate the vulnerable code, and show, in plain language, how an attacker could exploit it. At the end, we’ll link to the original sources and patches.

What is JFS?

JFS), or Journaled File System, was created by IBM for high-performance journaling on Linux and other Unix-like systems. Despite being less popular than EXT4 or XFS, it’s still used — especially on legacy systems.

The Flaw

The problem lies inside the JFS kernel code’s dbAdjTree function. This function manipulates data structures used for managing block allocation in the filesystem, specifically the dmt_stree array. Normally, when code accesses arrays, it should always check that the index is within bounds (that is, it doesn't go past the start or end of the array). In this case, there was no bounds checking, so it was possible to read or write past the array’s edge — with dangerous consequences.

This is formally called an array index out of bounds error.

Original References

- lkml Patch Discussion: Array Index Out-Of-Bounds in dbAdjTree
- CVE Record NVD - CVE-2023-52601

Let’s look at a simplified version of the vulnerable code

void dbAdjTree(struct dmaptree *tp, int stree_index, ... )
{
    // The vulnerable access
    struct dmapstor *sp = &tp->dmt_stree[stree_index];
    ...
}

Notice that stree_index can be set to any value by functions calling dbAdjTree, but there’s no check to ensure that stree_index is valid (i.e., inside and whatever the size of dmt_stree is).

If, say, dmt_stree is an array of 4 elements and the code calls dbAdjTree(tp, 7, ...), the code would access memory outside the array.

If an attacker can control or influence the stree_index, they might point this to sensitive parts of memory.

How was it fixed?

The patch introduced a bounds check. It also added a boolean (is_ctl) for newer logic, making the code more robust.

Here’s patch-like pseudocode of the fix

void dbAdjTree(struct dmaptree *tp, int stree_index, bool is_ctl, ...)
{
    int max_index = is_ctl ? ARRAY_SIZE(tp->dmt_stree_ctl) : ARRAY_SIZE(tp->dmt_stree);
    if (stree_index <  || stree_index >= max_index) {
        // Prevent out of bounds access
        return;
    }
    struct dmapstor *sp = is_ctl ? &tp->dmt_stree_ctl[stree_index] : &tp->dmt_stree[stree_index];
    ...
}

Now, if the code attempts to access an invalid index, the function just exits.

“Exploiting” in Plain English

An array index out of bounds bug is a classic type of memory safety issue. If user data, or a specially-crafted filesystem image, can control the value that ends up as stree_index, an attacker can:

1. Crash the Kernel (Denial of Service): By accessing memory the kernel shouldn’t, leading to an OOPS or panic.
2. Read/Write Arbitrary Kernel Memory: In some cases, this lets them see or modify other kernel data – possibly leading to unauthorized behavior.
3. Escalate Privileges: By overwriting critical data, the attacker could convert a normal bug into code execution (much harder, but possible).

Example Exploit (Conceptual)

Suppose an attacker provides a filesystem image with a header or block values that result in stree_index being, say, 100 instead of –3. When the kernel loads this image, it tries to access dmt_stree[100]. Data at tp + 100 * sizeof(dmapstor) is now being accessed.

Pseudocode

// Let's assume kernel parses some metadata 'n'
int n = READ_FROM_DISK();  // attacker controls this value!
dbAdjTree(tp, n, ...);

If the attacker crafts a corrupted image and loads/mounts it, the kernel could crash or do unexpected things — all triggered by the attacker.

Proof of Concept

A real exploit would require knowledge of the kernel's memory layout, but here is a conceptual userland code snippet to trigger the bug in a lab (using a purposely corrupted JFS image):

# psuedocode: Craft a JFS image with corrupted metadata
with open('corrupt_jfs.img', 'rb+') as f:
    f.seek(OFFSET_TO_TREE_INDEX)
    f.write( struct.pack("<I", 99999) ) # exaggerated index

# Now, mount this as a JFS filesystem in a controlled VM and observe kernel crash
os.system('mount -t jfs -o loop,rw corrupt_jfs.img /mnt/test')

(Warning: Don’t run this on production systems!)

Prevention

If you don’t use JFS, you are unaffected. But for those that do, update your Linux kernel immediately to a version including the patch! All major distributions now include it.

Conclusion

CVE-2023-52601 was a classic type of C programming mistake — missing a simple bounds check on an array. The Linux kernel team quickly fixed it as soon as it was found.

- If you administer Linux systems, make sure your kernels are up-to-date, especially if you use JFS anywhere.
- If you are learning about security, this is a great case study of how simple bugs can have deep impacts.

References & Further Reading

- Original Patchmail
- National Vulnerability Database - CVE-2023-52601
- JFS Wiki)
- Linux Kernel Mailing List (LKML)

Timeline

Published on: 03/06/2024 07:15:10 UTC
Last modified on: 11/12/2024 19:35:02 UTC