Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed404b2b authored by Vaishnavi AVS's avatar Vaishnavi AVS
Browse files

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: default avatarPraveen Talari <quic_ptalari@quicinc.com>
Signed-off-by: default avatarVaishnavi AVS <quic_vavs@quicinc.com>
parent 0b52a803
Loading
Loading
Loading
Loading
+51 −4
Original line number Diff line number Diff line
// 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 <linux/clk.h>
@@ -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,6 +1084,7 @@ 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
		}
	}
@@ -1268,7 +1275,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
		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);
	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) {