Skip to content

Commit 25af740

Browse files
Isaac ManjarresRussell King (Oracle)
Isaac Manjarres
authored and
Russell King (Oracle)
committed
ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a ("ARM: 9220/1: amba: Remove deferred device addition"), it became possible for amba_read_periphid() to be invoked concurrently from two threads for a particular AMBA device. Consider the case where a thread (T0) is registering an AMBA driver, and searching for all of the devices it can match with on the AMBA bus. Suppose that another thread (T1) is executing the deferred probe work, and is searching through all of the AMBA drivers on the bus for a driver that matches a particular AMBA device. Assume that both threads begin operating on the same AMBA device and the device's peripheral ID is still unknown. In this scenario, the amba_match() function will be invoked for the same AMBA device by both threads, which means amba_read_periphid() can also be invoked by both threads, and both threads will be able to manipulate the AMBA device's pclk pointer without any synchronization. It's possible that one thread will initialize the pclk pointer, then the other thread will re-initialize it, overwriting the previous value, and both will race to free the same pclk, resulting in a use-after-free for whichever thread frees the pclk last. Add a lock per AMBA device to synchronize the handling with detecting the peripheral ID to avoid the use-after-free scenario. The following KFENCE bug report helped detect this problem: ================================================================== BUG: KFENCE: use-after-free read in clk_disable+0x14/0x34 Use-after-free read at 0x(ptrval) (in kfence-Rust-for-Linux#19): clk_disable+0x14/0x34 amba_read_periphid+0xdc/0x134 amba_match+0x3c/0x84 __driver_attach+0x20/0x158 bus_for_each_dev+0x74/0xc0 bus_add_driver+0x154/0x1e8 driver_register+0x88/0x11c do_one_initcall+0x8c/0x2fc kernel_init_freeable+0x190/0x220 kernel_init+0x10/0x108 ret_from_fork+0x14/0x3c 0x0 kfence-Rust-for-Linux#19: 0x(ptrval)-0x(ptrval), size=36, cache=kmalloc-64 allocated by task 8 on cpu 0 at 11.629931s: clk_hw_create_clk+0x38/0x134 amba_get_enable_pclk+0x10/0x68 amba_read_periphid+0x28/0x134 amba_match+0x3c/0x84 __device_attach_driver+0x2c/0xc4 bus_for_each_drv+0x80/0xd0 __device_attach+0xb0/0x1f0 bus_probe_device+0x88/0x90 deferred_probe_work_func+0x8c/0xc0 process_one_work+0x23c/0x690 worker_thread+0x34/0x488 kthread+0xd4/0xfc ret_from_fork+0x14/0x3c 0x0 freed by task 8 on cpu 0 at 11.630095s: amba_read_periphid+0xec/0x134 amba_match+0x3c/0x84 __device_attach_driver+0x2c/0xc4 bus_for_each_drv+0x80/0xd0 __device_attach+0xb0/0x1f0 bus_probe_device+0x88/0x90 deferred_probe_work_func+0x8c/0xc0 process_one_work+0x23c/0x690 worker_thread+0x34/0x488 kthread+0xd4/0xfc ret_from_fork+0x14/0x3c 0x0 Cc: Saravana Kannan <saravanak@google.com> Cc: patches@armlinux.org.uk Fixes: f2d3b9a ("ARM: 9220/1: amba: Remove deferred device addition") Reported-by: Guenter Roeck <linux@roeck-us.net> Tested-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Isaac J. Manjarres <isaacmanjarres@google.com> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
1 parent b90cb10 commit 25af740

File tree

2 files changed

+8
-1
lines changed

2 files changed

+8
-1
lines changed

drivers/amba/bus.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ static int amba_match(struct device *dev, struct device_driver *drv)
209209
struct amba_device *pcdev = to_amba_device(dev);
210210
struct amba_driver *pcdrv = to_amba_driver(drv);
211211

212+
mutex_lock(&pcdev->periphid_lock);
212213
if (!pcdev->periphid) {
213214
int ret = amba_read_periphid(pcdev);
214215

@@ -218,11 +219,14 @@ static int amba_match(struct device *dev, struct device_driver *drv)
218219
* permanent failure in reading pid and cid, simply map it to
219220
* -EPROBE_DEFER.
220221
*/
221-
if (ret)
222+
if (ret) {
223+
mutex_unlock(&pcdev->periphid_lock);
222224
return -EPROBE_DEFER;
225+
}
223226
dev_set_uevent_suppress(dev, false);
224227
kobject_uevent(&dev->kobj, KOBJ_ADD);
225228
}
229+
mutex_unlock(&pcdev->periphid_lock);
226230

227231
/* When driver_override is set, only bind to the matching driver */
228232
if (pcdev->driver_override)
@@ -532,6 +536,7 @@ static void amba_device_release(struct device *dev)
532536

533537
if (d->res.parent)
534538
release_resource(&d->res);
539+
mutex_destroy(&d->periphid_lock);
535540
kfree(d);
536541
}
537542

@@ -584,6 +589,7 @@ static void amba_device_initialize(struct amba_device *dev, const char *name)
584589
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
585590
dev->dev.dma_parms = &dev->dma_parms;
586591
dev->res.name = dev_name(&dev->dev);
592+
mutex_init(&dev->periphid_lock);
587593
}
588594

589595
/**

include/linux/amba/bus.h

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct amba_device {
6767
struct clk *pclk;
6868
struct device_dma_parameters dma_parms;
6969
unsigned int periphid;
70+
struct mutex periphid_lock;
7071
unsigned int cid;
7172
struct amba_cs_uci_id uci;
7273
unsigned int irq[AMBA_NR_IRQS];

0 commit comments

Comments
 (0)