linux-stable/drivers/mtd/hyperbus/rpc-if.c
George G. Davis 5d39cd2059 mtd: hyperbus: rpc-if: fix bug in rpcif_hb_remove
[ Upstream commit baaf965f94 ]

The following KASAN BUG is observed when testing the rpc-if driver on
rcar-gen3:

root@rcar-gen3:~# modprobe -r rpc-if
[  101.930146] ==================================================================
[  101.937408] BUG: KASAN: slab-out-of-bounds in __lock_acquire+0x518/0x25d0
[  101.944240] Read of size 8 at addr ffff0004c5be2750 by task modprobe/664
[  101.950959]
[  101.952466] CPU: 2 PID: 664 Comm: modprobe Not tainted 5.14.0-rc1-00342-g1a1464d7aa31 #1
[  101.960578] Hardware name: Renesas H3ULCB board based on r8a77951 (DT)
[  101.967120] Call trace:
[  101.969580]  dump_backtrace+0x0/0x2c0
[  101.973275]  show_stack+0x1c/0x30
[  101.976616]  dump_stack_lvl+0x9c/0xd8
[  101.980301]  print_address_description.constprop.0+0x74/0x2b8
[  101.986071]  kasan_report+0x1f4/0x26c
[  101.989757]  __asan_load8+0x98/0xd4
[  101.993266]  __lock_acquire+0x518/0x25d0
[  101.997215]  lock_acquire.part.0+0x18c/0x360
[  102.001506]  lock_acquire+0x74/0x90
[  102.005013]  _raw_spin_lock_irq+0x98/0x130
[  102.009131]  __pm_runtime_disable+0x30/0x210
[  102.013427]  rpcif_hb_remove+0x5c/0x70 [rpc_if]
[  102.018001]  platform_remove+0x40/0x80
[  102.021771]  __device_release_driver+0x234/0x350
[  102.026412]  driver_detach+0x158/0x20c
[  102.030179]  bus_remove_driver+0xa0/0x140
[  102.034212]  driver_unregister+0x48/0x80
[  102.038153]  platform_driver_unregister+0x18/0x24
[  102.042879]  rpcif_platform_driver_exit+0x1c/0x34 [rpc_if]
[  102.048400]  __arm64_sys_delete_module+0x210/0x310
[  102.053212]  invoke_syscall+0x60/0x190
[  102.056986]  el0_svc_common+0x12c/0x144
[  102.060844]  do_el0_svc+0x88/0xac
[  102.064181]  el0_svc+0x24/0x3c
[  102.067257]  el0t_64_sync_handler+0x1a8/0x1b0
[  102.071634]  el0t_64_sync+0x198/0x19c
[  102.075315]
[  102.076815] Allocated by task 628:
[  102.080781]
[  102.082280] Last potentially related work creation:
[  102.087524]
[  102.089022] The buggy address belongs to the object at ffff0004c5be2000
[  102.089022]  which belongs to the cache kmalloc-2k of size 2048
[  102.101555] The buggy address is located 1872 bytes inside of
[  102.101555]  2048-byte region [ffff0004c5be2000, ffff0004c5be2800)
[  102.113486] The buggy address belongs to the page:
[  102.118409]
[  102.119908] Memory state around the buggy address:
[  102.124711]  ffff0004c5be2600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[  102.131947]  ffff0004c5be2680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[  102.139181] >ffff0004c5be2700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[  102.146412]                                                  ^
[  102.152257]  ffff0004c5be2780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[  102.159491]  ffff0004c5be2800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[  102.166723] ==================================================================

The above bug is caused by use of the wrong pointer in the
rpcif_disable_rpm() call. Fix the bug by using the correct pointer.

Fixes: 5de15b610f ("mtd: hyperbus: add Renesas RPC-IF driver")
Signed-off-by: George G. Davis <davis.george@siemens.com>
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Link: https://lore.kernel.org/r/20210716204935.25859-1-george_davis@mentor.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
2022-01-27 11:03:15 +01:00

172 lines
3.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Linux driver for RPC-IF HyperFlash
*
* Copyright (C) 2019-2020 Cogent Embedded, Inc.
*/
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/hyperbus.h>
#include <linux/mtd/mtd.h>
#include <linux/mux/consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <memory/renesas-rpc-if.h>
struct rpcif_hyperbus {
struct rpcif rpc;
struct hyperbus_ctlr ctlr;
struct hyperbus_device hbdev;
};
static const struct rpcif_op rpcif_op_tmpl = {
.cmd = {
.buswidth = 8,
.ddr = true,
},
.ocmd = {
.buswidth = 8,
.ddr = true,
},
.addr = {
.nbytes = 1,
.buswidth = 8,
.ddr = true,
},
.data = {
.buswidth = 8,
.ddr = true,
},
};
static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to,
unsigned long from, ssize_t len)
{
struct rpcif_op op = rpcif_op_tmpl;
op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM;
op.addr.val = from >> 1;
op.dummy.buswidth = 1;
op.dummy.ncycles = 15;
op.data.dir = RPCIF_DATA_IN;
op.data.nbytes = len;
op.data.buf.in = to;
rpcif_prepare(rpc, &op, NULL, NULL);
}
static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to,
void *from, ssize_t len)
{
struct rpcif_op op = rpcif_op_tmpl;
op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM;
op.addr.val = to >> 1;
op.data.dir = RPCIF_DATA_OUT;
op.data.nbytes = len;
op.data.buf.out = from;
rpcif_prepare(rpc, &op, NULL, NULL);
}
static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr)
{
struct rpcif_hyperbus *hyperbus =
container_of(hbdev, struct rpcif_hyperbus, hbdev);
map_word data;
rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2);
rpcif_manual_xfer(&hyperbus->rpc);
return data.x[0];
}
static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr,
u16 data)
{
struct rpcif_hyperbus *hyperbus =
container_of(hbdev, struct rpcif_hyperbus, hbdev);
rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2);
rpcif_manual_xfer(&hyperbus->rpc);
}
static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to,
unsigned long from, ssize_t len)
{
struct rpcif_hyperbus *hyperbus =
container_of(hbdev, struct rpcif_hyperbus, hbdev);
rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len);
rpcif_dirmap_read(&hyperbus->rpc, from, len, to);
}
static const struct hyperbus_ops rpcif_hb_ops = {
.read16 = rpcif_hb_read16,
.write16 = rpcif_hb_write16,
.copy_from = rpcif_hb_copy_from,
};
static int rpcif_hb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rpcif_hyperbus *hyperbus;
int error;
hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL);
if (!hyperbus)
return -ENOMEM;
error = rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent);
if (error)
return error;
platform_set_drvdata(pdev, hyperbus);
rpcif_enable_rpm(&hyperbus->rpc);
rpcif_hw_init(&hyperbus->rpc, true);
hyperbus->hbdev.map.size = hyperbus->rpc.size;
hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap;
hyperbus->ctlr.dev = dev;
hyperbus->ctlr.ops = &rpcif_hb_ops;
hyperbus->hbdev.ctlr = &hyperbus->ctlr;
hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL);
error = hyperbus_register_device(&hyperbus->hbdev);
if (error)
rpcif_disable_rpm(&hyperbus->rpc);
return error;
}
static int rpcif_hb_remove(struct platform_device *pdev)
{
struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev);
int error = hyperbus_unregister_device(&hyperbus->hbdev);
rpcif_disable_rpm(&hyperbus->rpc);
return error;
}
static struct platform_driver rpcif_platform_driver = {
.probe = rpcif_hb_probe,
.remove = rpcif_hb_remove,
.driver = {
.name = "rpc-if-hyperflash",
},
};
module_platform_driver(rpcif_platform_driver);
MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver");
MODULE_LICENSE("GPL v2");