[PATCH 3/6] bus: fsl-mc: fix double free of the root DPRC fsl-mc device

From: laurentiu . tudor
Date: Tue Feb 08 2022 - 10:00:03 EST


From: Laurentiu Tudor <laurentiu.tudor@xxxxxxx>

By simply calling remove() op from shutdown() op we end up
deleting twice the root DPRC fsl-mc device. This causes various
issues such as the crash below [1], on reboot. Re-arrange the code
in shutdown and remove callbacks so this does not happen any more.

Unable to handle kernel NULL pointer dereference at virtual address 0
Mem abort info:
ESR = 0x96000006
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x06: level 2 translation fault
Data abort info:
ISV = 0, ISS = 0x00000006
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=00000081086db000
Internal error: Oops: 96000006 [#1] PREEMPT SMP
Modules linked in:
CPU: 1 PID: 977 Comm: reboot Not tainted 5.14.0-rc1-00221
Hardware name: Freescale Layerscape 2088A RDB Board (DT)
pstate: 60000005 (nZCv daif -PAN -UAO -TCO BTYPE=--)
pc : sysfs_remove_link_from_group+0x28/0x94
lr : iommu_device_unlink+0x94/0xb4
sp : ffff800012b3b8c0
x29: ffff800012b3b8c0 x28: ffff24f8e061a940 x27: 0000000000000000
x26: 0000000000000000 x25: ffffb43783811228 x24: ffff24f840a882f0
x23: ffff24f8400cf000 x22: 0000000000000003 x21: ffffb43783bb6e10
x20: ffff24f8c9641040 x19: 0000000000000000 x18: ffffffffffffffff
x17: 3236366535672d31 x16: 323230302d316372 x15: ffff800092b3b537
x14: 0000000000000004 x13: 0000000000000000 x12: ffff24f840467af8
x11: ffff24f8404c7d98 x10: ffff24f840467908 x9 : ffff24f840064b98
x8 : ffff24f840467930 x7 : ffff24f8e061a940 x6 : ffff24f840453b30
x5 : 0000000000000001 x4 : 0000000000000000 x3 : ffffb43784442d28
x2 : ffff24f8c9641040 x1 : ffffb43783bb6e10 x0 : 0000000000000000
Call trace:
sysfs_remove_link_from_group+0x28/0x94
iommu_device_unlink+0x94/0xb4
iommu_release_device+0x34/0x94
iommu_bus_notifier+0xc0/0xd4
blocking_notifier_call_chain+0x70/0xac
device_del+0x2f4/0x424
fsl_mc_device_remove+0x4c/0x80
__fsl_mc_device_remove+0x14/0x2c
device_for_each_child+0x5c/0xac
dprc_remove+0x44/0x70
fsl_mc_driver_remove+0x4c/0xa0
__device_release_driver+0x188/0x22c
device_release_driver+0x30/0x50
bus_remove_device+0x10c/0x140
device_del+0x16c/0x424
fsl_mc_bus_remove+0xb8/0x160
fsl_mc_bus_shutdown+0x14/0x20
platform_shutdown+0x28/0x40
device_shutdown+0x15c/0x360
__do_sys_reboot+0x218/0x2a0
__arm64_sys_reboot+0x28/0x34
invoke_syscall+0x48/0x114
el0_svc_common+0x40/0xdc
do_el0_svc+0x2c/0x94
el0_svc+0x2c/0x54
el0t_64_sync_handler+0xa8/0x12c
el0t_64_sync+0x198/0x19c
Code: aa0203f4 f90013f5 aa0103f5 b5000060 (f9400002)
---[ end trace 1a98489358f432bb ]---
/etc/rc6.d/S90reboot: line 15: 977 Segmentation fault reboot -d -f

Signed-off-by: Laurentiu Tudor <laurentiu.tudor@xxxxxxx>
---
drivers/bus/fsl-mc/fsl-mc-bus.c | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index 8cbac1b4b60e..459947988e0d 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -1230,21 +1230,18 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
}

/*
- * fsl_mc_bus_remove - callback invoked when the root MC bus is being
- * removed
+ * fsl_mc_bus_shutdown - callback invoked when the root MC bus is being
+ * shutdown
*/
-static int fsl_mc_bus_remove(struct platform_device *pdev)
+static void fsl_mc_bus_shutdown(struct platform_device *pdev)
{
struct fsl_mc *mc = platform_get_drvdata(pdev);
struct fsl_mc_io *mc_io;

if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))
- return -EINVAL;
+ return;

mc_io = mc->root_mc_bus_dev->mc_io;
-
- fsl_mc_device_remove(mc->root_mc_bus_dev);
-
fsl_destroy_mc_io(mc_io);

bus_unregister_notifier(&fsl_mc_bus_type, &fsl_mc_nb);
@@ -1258,13 +1255,24 @@ static int fsl_mc_bus_remove(struct platform_device *pdev)
(GCR1_P1_STOP | GCR1_P2_STOP),
mc->fsl_mc_regs + FSL_MC_GCR1);
}
-
- return 0;
}

-static void fsl_mc_bus_shutdown(struct platform_device *pdev)
+/*
+ * fsl_mc_bus_remove - callback invoked when the root MC bus is being
+ * removed
+ */
+static int fsl_mc_bus_remove(struct platform_device *pdev)
{
- fsl_mc_bus_remove(pdev);
+ struct fsl_mc *mc = platform_get_drvdata(pdev);
+
+ if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))
+ return -EINVAL;
+
+ fsl_mc_device_remove(mc->root_mc_bus_dev);
+
+ fsl_mc_bus_shutdown(pdev);
+
+ return 0;
}

static const struct of_device_id fsl_mc_bus_match_table[] = {
--
2.17.1