The security of the Linux kernel depends on every detail being just right. Sometimes, a small mistake in the order of operations can open up a nasty security hole—like the one fixed in CVE-2021-47549. This bug affected systems running PowerPC64 GNU/Linux and involved a use-after-free (UAF) in the sata_fsl driver when unloading the module (rmmod sata_fsl.ko). In this exclusive long read, we’ll help you understand how the bug happens, how to exploit it, and how the Linux patch squashes the danger.

The Bug: What Went Wrong?

Let’s break down the vulnerability. The key point is a use-after-free (UAF) scenario, which is when code uses memory it just freed. UAFs often let attackers manipulate kernel memory or trigger stability issues.

Here, the failing sequence was triggered by removing the sata_fsl module from the Linux kernel using rmmod.

What is sata_fsl?

sata_fsl is the FreeScale (now NXP) SATA controller driver found in Linux’s drivers/ata/. It’s specific to certain ARM/PowerPC SoCs. Normally, drivers clean up their resources carefully when removed, using routines like remove, host_stop, and port_stop.

But in this kernel module, the unfriendly use-after-free occurs around the driver’s remove/unregister routine and the order it frees hardware resources and memory. Here’s the bug trace:

BUG: Unable to handle kernel data access on read at x80000800805b502c
Oops: Kernel access of bad area, sig: 11 [#1]
NIP [c0000000000388a4] .ioread32+x4/x20
LR [80000000000c6034] .sata_fsl_port_stop+x44/xe [sata_fsl]
...
        drv->remove(dev) --> sata_fsl_remove
            iounmap(host_priv->hcr_base)   <---- unmap
            kfree(host_priv);              <---- free
    devres_release_all
      release_nodes
        dr->node.release(dev, dr->data) --> ata_host_stop
           ap->ops->port_stop(ap) --> sata_fsl_port_stop
               ioread32(hcr_base + HCONTROL)   <---- UAF

What’s the issue?

2. kfree() — frees the structure.

- But *after* these are called, port_stop() calls ioread32() on the unmapped/freed memory, causing a UAF crash—or, for attackers, a chance at exploitation!

Original Remove Function

static int sata_fsl_remove(struct platform_device *pdev)
{
    struct ata_host *host = platform_get_drvdata(pdev);
    struct fsl_sata_host_priv *host_priv = host->private_data;

    iounmap(host_priv->hcr_base);
    kfree(host_priv);

    return ata_platform_remove_one(pdev);
}

Original Port Stop

static void sata_fsl_port_stop(struct ata_port *ap)
{
    struct fsl_sata_host_priv *host_priv = ap->host->private_data;
    ioread32(host_priv->hcr_base + HCONTROL); // UAF if host_priv is invalid!
    // Other port stop operations
}

Attack flow

- An attacker (as root, since only root can unload kernel modules) could keep the kernel busy, and time the memory allocation, hoping to reclaim the just-freed memory for malicious data (kernel heap spraying), causing trusted code to dereference attacker-supplied data.

Although this bug is local only (since you have to unload the module), it’s not unheard of that local priv esc attackers would use such bugs in chained exploits.

The Fix (and How It Works)

The main idea is: Don’t free or unmap resources in remove! Instead, do so after all cleanups are done, especially after port_stop. The fix moves the releasing code into a host_stop function, which is called after port_stop is done safely.

Patch Extract

-static int sata_fsl_remove(struct platform_device *pdev)
-{
-    struct ata_host *host = platform_get_drvdata(pdev);
-    struct fsl_sata_host_priv *host_priv = host->private_data;
-
-    iounmap(host_priv->hcr_base);
-    kfree(host_priv);
-
-    return ata_platform_remove_one(pdev);
-}
+static void sata_fsl_host_stop(struct ata_host *host)
+{
+    struct fsl_sata_host_priv *host_priv = host->private_data;
+    iounmap(host_priv->hcr_base);
+    kfree(host_priv);
+}

Then, in device setup, .host_stop = sata_fsl_host_stop, so freeing only happens after all port/host cleanup is done.

ata_host_stop calls port_stop (now safe, host_priv valid)

2. host_stop frees/unmaps host_priv memory

Simple PoC to Trigger the Bug

If you have kernel debug builds (CONFIG_DEBUG_SLAB, etc.), just running rmmod sata_fsl on PowerPC64 would crash due to the UAF. Example script:

#!/bin/bash
sudo modprobe sata_fsl        # Load the driver
# ...do something that causes SATA use...
sudo rmmod sata_fsl           # UAF triggers here; see dmesg or crash!

Upstream Patch:

kernel.org commit

CVE Record:

MITRE CVE-2021-47549

Linux Bugzilla:

Bug 215015

Kernel UAF notes:

Understanding UAF in Linux

Timely and careful patching protects your system, especially with drivers.

For system administrators: Always update your kernel, especially on hardware-specific builds! For kernel developers, this is a classic lesson in resource ordering and safe teardown procedures.

Timeline

Published on: 05/24/2024 15:15:19 UTC
Last modified on: 01/07/2025 17:04:16 UTC