From ed404b2b80a1acb4e09c7aedcbc593f54791bf7e Mon Sep 17 00:00:00 2001 From: Vaishnavi AVS Date: Mon, 30 Jan 2023 11:06:27 +0530 Subject: [PATCH] i2c-msm-geni: KASAN: use-after-free in __list_add_valid+0x2c/0xc4 This UAF issue is seen when driver is removed and inserted. During driver removal, pm runtime resume callback invoked in which as part of clock, ab/ib nodes are added in common struct geni_se_dev. As part of driver exit, we are not removing the ab/ib list from common structure list due to which the issue is seen when driver is loaded. As part of driver removal, checking the status of runtime suspend if it is not suspended, invoke geni suspend call otherwise ignore. So by suspend call ensured that ab/ib are removed from lists, so that UAF will not be encountered when next load of driver. Change-Id: I1f0c7a29c5e268a1ab5c017e271ad0484dcab24f Signed-off-by: Praveen Talari Signed-off-by: Vaishnavi AVS --- drivers/i2c/busses/i2c-msm-geni.c | 55 ++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-msm-geni.c b/drivers/i2c/busses/i2c-msm-geni.c index 3a1964dfaac1..473ff18b0f2e 100644 --- a/drivers/i2c/busses/i2c-msm-geni.c +++ b/drivers/i2c/busses/i2c-msm-geni.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -136,10 +137,12 @@ struct geni_i2c_dev { bool disable_dma_mode; bool prev_cancel_pending; //Halt cancel till IOS in good state bool is_i2c_rtl_based; /* doing pending cancel only for rtl based SE's */ + atomic_t is_xfer_in_progress; /* Used to maintain xfer inprogress status */ }; static struct geni_i2c_dev *gi2c_dev_dbg[MAX_SE]; static int arr_idx; +static int geni_i2c_runtime_suspend(struct device *dev); struct geni_i2c_err_log { int err; @@ -1052,11 +1055,13 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, int i, ret = 0, timeout = 0; gi2c->err = 0; + atomic_set(&gi2c->is_xfer_in_progress, 1); /* Client to respect system suspend */ if (!pm_runtime_enabled(gi2c->dev)) { GENI_SE_ERR(gi2c->ipcl, false, gi2c->dev, "%s: System suspended\n", __func__); + atomic_set(&gi2c->is_xfer_in_progress, 0); return -EACCES; } @@ -1068,6 +1073,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, pm_runtime_put_noidle(gi2c->dev); /* Set device in suspended since resume failed */ pm_runtime_set_suspended(gi2c->dev); + atomic_set(&gi2c->is_xfer_in_progress, 0); return ret; } } @@ -1078,12 +1084,13 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, if (ret) { pm_runtime_mark_last_busy(gi2c->dev); pm_runtime_put_autosuspend(gi2c->dev); + atomic_set(&gi2c->is_xfer_in_progress, 0); return ret; //Don't perform xfer is cancel failed } } GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, - "n:%d addr:0x%x\n", num, msgs[0].addr); + "n:%d addr:0x%x\n", num, msgs[0].addr); gi2c->dbg_num = num; kfree(gi2c->dbg_buf_ptr); @@ -1268,7 +1275,7 @@ geni_i2c_txn_ret: pm_runtime_mark_last_busy(gi2c->dev); pm_runtime_put_autosuspend(gi2c->dev); } - + atomic_set(&gi2c->is_xfer_in_progress, 0); gi2c->cur = NULL; GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "i2c txn ret:%d, num:%d, err:%d\n", ret, num, gi2c->err); @@ -1476,10 +1483,10 @@ static int geni_i2c_probe(struct platform_device *pdev) return ret; } + atomic_set(&gi2c->is_xfer_in_progress, 0); snprintf(boot_marker, sizeof(boot_marker), - "M - DRIVER GENI_I2C_%d Ready", gi2c->adap.nr); + "M - DRIVER GENI_I2C_%d Ready", gi2c->adap.nr); place_marker(boot_marker); - dev_info(gi2c->dev, "I2C probed\n"); return 0; } @@ -1489,6 +1496,33 @@ static int geni_i2c_remove(struct platform_device *pdev) struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev); int i; + if (atomic_read(&gi2c->is_xfer_in_progress)) { + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "%s: Xfer is in progress\n", __func__); + return -EBUSY; + } + + if (!pm_runtime_status_suspended(gi2c->dev)) { + if (geni_i2c_runtime_suspend(gi2c->dev)) + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "%s: runtime suspend failed\n", __func__); + } + + if (gi2c->se_mode == GSI_ONLY) { + if (gi2c->tx_c) { + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "%s: clearing tx dma resource\n", __func__); + dma_release_channel(gi2c->tx_c); + } + if (gi2c->rx_c) { + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "%s: clearing rx dma resource\n", __func__); + dma_release_channel(gi2c->rx_c); + } + } + + pm_runtime_put_noidle(gi2c->dev); + pm_runtime_set_suspended(gi2c->dev); pm_runtime_disable(gi2c->dev); i2c_del_adapter(&gi2c->adap); @@ -1594,6 +1628,19 @@ static int geni_i2c_suspend_late(struct device *device) int ret; GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n", __func__); + + if (atomic_read(&gi2c->is_xfer_in_progress)) { + if (!pm_runtime_status_suspended(gi2c->dev)) { + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + ":%s: runtime PM is active\n", __func__); + return -EBUSY; + } + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "%s System suspend not allowed while xfer in progress\n", + __func__); + return -EBUSY; + } + /* Make sure no transactions are pending */ ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT); if (!ret) {