From 39d1fb94fa8075b273cd2a42dcfa86b52c22112e Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 25 Mar 2025 10:16:40 +0800 Subject: [PATCH] media: rockchip: vpss: prevent NULL dereference in rkvpss runtime resume - Validate vpss[i] and sw_base_addr before memset/memcpy operations - Add bounds checking for dev_num to prevent array overflow - Add error logging for invalid sw_base_addr during register restoration This resolves kernel panic caused by accessing uninitialized sw_base_addr during power management resume path. The issue occurred when restoring VPSS hardware registers from suspend state. Boot up on as below logs: [ 6.869681] lr : rkvpss_hw_runtime_resume+0x160/0x198 [ 6.870141] sp : ffffffc00969b930 [ 6.870444] x29: ffffffc00969b930 x28: ffffff80035e9800 x27: ffffff80020ad200 [ 6.871088] x26: 0000000000000003 x25: ffffffc0091f4000 x24: ffffff8001ed40a8 [ 6.871730] x23: ffffffc008cbf000 x22: ffffff8001953010 x21: ffffff8001ed40c8 [ 6.872373] x20: 0000000000000002 x19: ffffff8001ed4080 x18: 0000000000000000 [ 6.873018] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 [ 6.873652] x14: ffffff8001fb5025 x13: 0000000000000007 x12: ffffff800142c900 [ 6.874284] x11: 0000000000000002 x10: 00000007305dcf59 x9 : 0000000000000000 [ 6.874926] x8 : 0000018000000000 x7 : 0000000000000000 x6 : 000000000000003f [ 6.875567] x5 : 0000000000000040 x4 : 0000000000000000 x3 : 0000000000000004 [ 6.876208] x2 : 0000000000006f40 x1 : 0000000000000000 x0 : 0000018000000000 [ 6.876851] Call trace: [ 6.877076] __memset+0x16c/0x188 [ 6.877375] pm_generic_runtime_resume+0x24/0x34 [ 6.877787] __rpm_callback+0x40/0x11c [ 6.878129] rpm_callback+0x68/0x6c [ 6.878448] rpm_resume+0x304/0x37c [ 6.878767] __pm_runtime_resume+0x50/0x74 [ 6.879132] ofl_open+0x4c/0xc8 [ 6.879419] v4l2_open+0x98/0xec [ 6.879714] chrdev_open+0x158/0x164 [ 6.880048] do_dentry_open+0x2d4/0x350 Change-Id: Iab179899258189d7bdf9ccc9bb13d07c25d80910 Signed-off-by: Caesar Wang Signed-off-by: Cai YiWei --- drivers/media/platform/rockchip/vpss/common.c | 10 ++++++++++ drivers/media/platform/rockchip/vpss/common.h | 1 + drivers/media/platform/rockchip/vpss/dev.c | 7 +++++-- drivers/media/platform/rockchip/vpss/hw.c | 15 ++++++++++----- drivers/media/platform/rockchip/vpss/hw.h | 1 + drivers/media/platform/rockchip/vpss/stream_v10.c | 3 ++- drivers/media/platform/rockchip/vpss/stream_v20.c | 3 ++- .../platform/rockchip/vpss/vpss_offline_v10.c | 3 +++ .../platform/rockchip/vpss/vpss_offline_v20.c | 2 +- 9 files changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/rockchip/vpss/common.c b/drivers/media/platform/rockchip/vpss/common.c index ea5c21fe01d8..03ed323f1fbc 100644 --- a/drivers/media/platform/rockchip/vpss/common.c +++ b/drivers/media/platform/rockchip/vpss/common.c @@ -165,3 +165,13 @@ int rkvpss_attach_hw(struct rkvpss_device *vpss) return 0; } + +void rkvpss_detach_hw(struct rkvpss_device *vpss) +{ + struct rkvpss_hw_dev *hw = vpss->hw_dev; + + if (hw) { + hw->vpss[vpss->dev_id] = NULL; + vpss->hw_dev = NULL; + } +} diff --git a/drivers/media/platform/rockchip/vpss/common.h b/drivers/media/platform/rockchip/vpss/common.h index fb99bf0a05c4..46bb55bd2d57 100644 --- a/drivers/media/platform/rockchip/vpss/common.h +++ b/drivers/media/platform/rockchip/vpss/common.h @@ -123,5 +123,6 @@ void rkvpss_unite_clear_bits(struct rkvpss_device *dev, u32 reg, u32 mask); void rkvpss_update_regs(struct rkvpss_device *dev, u32 start, u32 end); int rkvpss_attach_hw(struct rkvpss_device *vpss); +void rkvpss_detach_hw(struct rkvpss_device *vpss); void rkvpss_set_clk_rate(struct clk *clk, unsigned long rate); #endif diff --git a/drivers/media/platform/rockchip/vpss/dev.c b/drivers/media/platform/rockchip/vpss/dev.c index d1be6bb1d865..bf48377b9d96 100644 --- a/drivers/media/platform/rockchip/vpss/dev.c +++ b/drivers/media/platform/rockchip/vpss/dev.c @@ -307,7 +307,7 @@ static int rkvpss_plat_probe(struct platform_device *pdev) ret = v4l2_device_register(vpss_dev->dev, v4l2_dev); if (ret < 0) { v4l2_err(v4l2_dev, "register v4l2 device failed:%d\n", ret); - return ret; + goto err_detach; } media_device_init(&vpss_dev->media_dev); ret = media_device_register(&vpss_dev->media_dev); @@ -324,16 +324,18 @@ static int rkvpss_plat_probe(struct platform_device *pdev) atomic_set(&vpss_dev->pipe_stream_cnt, 0); rkvpss_proc_init(vpss_dev); pm_runtime_enable(&pdev->dev); - vpss_dev->is_probe_end = true; init_waitqueue_head(&vpss_dev->stop_done); vpss_dev->is_suspend = false; vpss_dev->is_idle = true; + vpss_dev->is_probe_end = true; return 0; err_unreg_media_dev: media_device_unregister(&vpss_dev->media_dev); err_unreg_v4l2_dev: v4l2_device_unregister(&vpss_dev->v4l2_dev); +err_detach: + rkvpss_detach_hw(vpss_dev); return ret; } @@ -353,6 +355,7 @@ static int rkvpss_plat_remove(struct platform_device *pdev) media_device_unregister(&vpss_dev->media_dev); v4l2_device_unregister(&vpss_dev->v4l2_dev); mutex_destroy(&vpss_dev->apilock); + rkvpss_detach_hw(vpss_dev); return 0; } diff --git a/drivers/media/platform/rockchip/vpss/hw.c b/drivers/media/platform/rockchip/vpss/hw.c index 5b48d716083c..ac560a660823 100644 --- a/drivers/media/platform/rockchip/vpss/hw.c +++ b/drivers/media/platform/rockchip/vpss/hw.c @@ -854,7 +854,6 @@ static int rkvpss_hw_probe(struct platform_device *pdev) hw_dev = devm_kzalloc(dev, sizeof(*hw_dev), GFP_KERNEL); if (!hw_dev) return -ENOMEM; - hw_dev->sw_reg = devm_kzalloc(dev, RKVPSS_SW_REG_SIZE, GFP_KERNEL); if (!hw_dev->sw_reg) return -ENOMEM; @@ -950,7 +949,7 @@ static int rkvpss_hw_probe(struct platform_device *pdev) rkvpss_register_offline(hw_dev); pm_runtime_enable(&pdev->dev); - + hw_dev->is_probe_end = true; return 0; err: return ret; @@ -1023,10 +1022,16 @@ static int __maybe_unused rkvpss_hw_runtime_resume(struct device *dev) if (dev->power.runtime_status) { for (i = 0; i < hw_dev->dev_num; i++) { - void *buf = hw_dev->vpss[i]->sw_base_addr; + struct rkvpss_device *vpss = hw_dev->vpss[i]; - memset(buf, 0, RKVPSS_SW_REG_SIZE_MAX); - memcpy_fromio(buf, base, RKVPSS_SW_REG_SIZE); + if (!vpss || !vpss->is_probe_end) + continue; + if (vpss->sw_base_addr) { + void *buf = vpss->sw_base_addr; + + memset(buf, 0, RKVPSS_SW_REG_SIZE_MAX); + memcpy_fromio(buf, base, RKVPSS_SW_REG_SIZE); + } } hw_dev->is_first = true; } else { diff --git a/drivers/media/platform/rockchip/vpss/hw.h b/drivers/media/platform/rockchip/vpss/hw.h index 2b58e3649a4d..fc15d667aba3 100644 --- a/drivers/media/platform/rockchip/vpss/hw.h +++ b/drivers/media/platform/rockchip/vpss/hw.h @@ -69,6 +69,7 @@ struct rkvpss_hw_dev { bool is_shutdown; bool is_suspend; bool is_first; + bool is_probe_end; }; #ifdef CONFIG_VIDEO_ROCKCHIP_VPSS_V10 diff --git a/drivers/media/platform/rockchip/vpss/stream_v10.c b/drivers/media/platform/rockchip/vpss/stream_v10.c index 6a9b28b8188e..b0b35f26c661 100644 --- a/drivers/media/platform/rockchip/vpss/stream_v10.c +++ b/drivers/media/platform/rockchip/vpss/stream_v10.c @@ -2075,7 +2075,8 @@ static int rkvpss_fh_open(struct file *file) struct rkvpss_device *dev = stream->dev; int ret; - if (!dev->is_probe_end) + if (!dev || !dev->is_probe_end || + !dev->hw_dev || !dev->hw_dev->is_probe_end) return -EINVAL; ret = v4l2_fh_open(file); diff --git a/drivers/media/platform/rockchip/vpss/stream_v20.c b/drivers/media/platform/rockchip/vpss/stream_v20.c index d1daf3df03b2..fc0b2c7addcf 100644 --- a/drivers/media/platform/rockchip/vpss/stream_v20.c +++ b/drivers/media/platform/rockchip/vpss/stream_v20.c @@ -2221,7 +2221,8 @@ static int rkvpss_fh_open(struct file *file) struct rkvpss_device *dev = stream->dev; int ret; - if (!dev->is_probe_end) + if (!dev || !dev->is_probe_end || + !dev->hw_dev || !dev->hw_dev->is_probe_end) return -EINVAL; ret = v4l2_fh_open(file); diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_v10.c b/drivers/media/platform/rockchip/vpss/vpss_offline_v10.c index c063459d0594..58d23b76320c 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline_v10.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v10.c @@ -2161,6 +2161,9 @@ static int ofl_open(struct file *file) struct rkvpss_offline_dev *ofl = video_drvdata(file); int ret; + if (!ofl || !ofl->hw || !ofl->hw->is_probe_end) + return -EINVAL; + ret = v4l2_fh_open(file); if (ret) goto end; diff --git a/drivers/media/platform/rockchip/vpss/vpss_offline_v20.c b/drivers/media/platform/rockchip/vpss/vpss_offline_v20.c index dbfbe05feddc..856eda7dd832 100644 --- a/drivers/media/platform/rockchip/vpss/vpss_offline_v20.c +++ b/drivers/media/platform/rockchip/vpss/vpss_offline_v20.c @@ -2548,7 +2548,7 @@ static int ofl_open(struct file *file) struct rkvpss_offline_dev *ofl = video_drvdata(file); int ret; - if (!ofl || !ofl->hw) { + if (!ofl || !ofl->hw || !ofl->hw->is_probe_end) { ret = -ENODEV; goto end; }