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:
CVE Record:
Linux Bugzilla:
Kernel UAF notes:
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