linux-stable/include/linux/amba/bus.h
Isaac Manjarres 25af7406df ARM: 9229/1: amba: Fix use-after-free in amba_read_periphid()
After commit f2d3b9a46e ("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-#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-#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: f2d3b9a46e ("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>
2022-08-30 11:12:52 +01:00

197 lines
6.2 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/include/amba/bus.h
*
* This device type deals with ARM PrimeCells and anything else that
* presents a proper CID (0xB105F00D) at the end of the I/O register
* region or that is derived from a PrimeCell.
*
* Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
*/
#ifndef ASMARM_AMBA_H
#define ASMARM_AMBA_H
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/err.h>
#include <linux/resource.h>
#include <linux/regulator/consumer.h>
#define AMBA_NR_IRQS 9
#define AMBA_CID 0xb105f00d
#define CORESIGHT_CID 0xb105900d
/*
* CoreSight Architecture specification updates the ID specification
* for components on the AMBA bus. (ARM IHI 0029E)
*
* Bits 15:12 of the CID are the device class.
*
* Class 0xF remains for PrimeCell and legacy components. (AMBA_CID above)
* Class 0x9 defines the component as CoreSight (CORESIGHT_CID above)
* Class 0x0, 0x1, 0xB, 0xE define components that do not have driver support
* at present.
* Class 0x2-0x8,0xA and 0xD-0xD are presently reserved.
*
* Remaining CID bits stay as 0xb105-00d
*/
/**
* Class 0x9 components use additional values to form a Unique Component
* Identifier (UCI), where peripheral ID values are identical for different
* components. Passed to the amba bus code from the component driver via
* the amba_id->data pointer.
* @devarch : coresight devarch register value
* @devarch_mask: mask bits used for matching. 0 indicates UCI not used.
* @devtype : coresight device type value
* @data : additional driver data. As we have usurped the original
* pointer some devices may still need additional data
*/
struct amba_cs_uci_id {
unsigned int devarch;
unsigned int devarch_mask;
unsigned int devtype;
void *data;
};
/* define offsets for registers used by UCI */
#define UCI_REG_DEVTYPE_OFFSET 0xFCC
#define UCI_REG_DEVARCH_OFFSET 0xFBC
struct clk;
struct amba_device {
struct device dev;
struct resource res;
struct clk *pclk;
struct device_dma_parameters dma_parms;
unsigned int periphid;
struct mutex periphid_lock;
unsigned int cid;
struct amba_cs_uci_id uci;
unsigned int irq[AMBA_NR_IRQS];
/*
* Driver name to force a match. Do not set directly, because core
* frees it. Use driver_set_override() to set or clear it.
*/
const char *driver_override;
};
struct amba_driver {
struct device_driver drv;
int (*probe)(struct amba_device *, const struct amba_id *);
void (*remove)(struct amba_device *);
void (*shutdown)(struct amba_device *);
const struct amba_id *id_table;
/*
* For most device drivers, no need to care about this flag as long as
* all DMAs are handled through the kernel DMA API. For some special
* ones, for example VFIO drivers, they know how to manage the DMA
* themselves and set this flag so that the IOMMU layer will allow them
* to setup and manage their own I/O address space.
*/
bool driver_managed_dma;
};
/*
* Constants for the designer field of the Peripheral ID register. When bit 7
* is set to '1', bits [6:0] should be the JEP106 manufacturer identity code.
*/
enum amba_vendor {
AMBA_VENDOR_ARM = 0x41,
AMBA_VENDOR_ST = 0x80,
AMBA_VENDOR_QCOM = 0x51,
AMBA_VENDOR_LSI = 0xb6,
};
extern struct bus_type amba_bustype;
#define to_amba_device(d) container_of(d, struct amba_device, dev)
#define amba_get_drvdata(d) dev_get_drvdata(&d->dev)
#define amba_set_drvdata(d,p) dev_set_drvdata(&d->dev, p)
#ifdef CONFIG_ARM_AMBA
int amba_driver_register(struct amba_driver *);
void amba_driver_unregister(struct amba_driver *);
#else
static inline int amba_driver_register(struct amba_driver *drv)
{
return -EINVAL;
}
static inline void amba_driver_unregister(struct amba_driver *drv)
{
}
#endif
struct amba_device *amba_device_alloc(const char *, resource_size_t, size_t);
void amba_device_put(struct amba_device *);
int amba_device_add(struct amba_device *, struct resource *);
int amba_device_register(struct amba_device *, struct resource *);
void amba_device_unregister(struct amba_device *);
int amba_request_regions(struct amba_device *, const char *);
void amba_release_regions(struct amba_device *);
/* Some drivers don't use the struct amba_device */
#define AMBA_CONFIG_BITS(a) (((a) >> 24) & 0xff)
#define AMBA_REV_BITS(a) (((a) >> 20) & 0x0f)
#define AMBA_MANF_BITS(a) (((a) >> 12) & 0xff)
#define AMBA_PART_BITS(a) ((a) & 0xfff)
#define amba_config(d) AMBA_CONFIG_BITS((d)->periphid)
#define amba_rev(d) AMBA_REV_BITS((d)->periphid)
#define amba_manf(d) AMBA_MANF_BITS((d)->periphid)
#define amba_part(d) AMBA_PART_BITS((d)->periphid)
#define __AMBA_DEV(busid, data, mask) \
{ \
.coherent_dma_mask = mask, \
.init_name = busid, \
.platform_data = data, \
}
/*
* APB devices do not themselves have the ability to address memory,
* so DMA masks should be zero (much like USB peripheral devices.)
* The DMA controller DMA masks should be used instead (much like
* USB host controllers in conventional PCs.)
*/
#define AMBA_APB_DEVICE(name, busid, id, base, irqs, data) \
struct amba_device name##_device = { \
.dev = __AMBA_DEV(busid, data, 0), \
.res = DEFINE_RES_MEM(base, SZ_4K), \
.irq = irqs, \
.periphid = id, \
}
/*
* AHB devices are DMA capable, so set their DMA masks
*/
#define AMBA_AHB_DEVICE(name, busid, id, base, irqs, data) \
struct amba_device name##_device = { \
.dev = __AMBA_DEV(busid, data, ~0ULL), \
.res = DEFINE_RES_MEM(base, SZ_4K), \
.irq = irqs, \
.periphid = id, \
}
/*
* module_amba_driver() - Helper macro for drivers that don't do anything
* special in module init/exit. This eliminates a lot of boilerplate. Each
* module may only use this macro once, and calling it replaces module_init()
* and module_exit()
*/
#define module_amba_driver(__amba_drv) \
module_driver(__amba_drv, amba_driver_register, amba_driver_unregister)
/*
* builtin_amba_driver() - Helper macro for drivers that don't do anything
* special in driver initcall. This eliminates a lot of boilerplate. Each
* driver may only use this macro once, and calling it replaces the instance
* device_initcall().
*/
#define builtin_amba_driver(__amba_drv) \
builtin_driver(__amba_drv, amba_driver_register)
#endif