[PATCH] soc: qcom: ice: Fix storing raw error pointers in XArray

Guangshuo Li posted 1 patch 3 hours ago
drivers/soc/qcom/ice.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
[PATCH] soc: qcom: ice: Fix storing raw error pointers in XArray
Posted by Guangshuo Li 3 hours ago
qcom_ice_probe() stores probe failures in ice_handles so that
of_qcom_ice_get() can distinguish between an ICE device that has not
probed yet and one that has failed permanently.

However, the failure paths store raw ERR_PTR() values in the XArray.
XArray entries use low pointer bits for internal encodings, so some
ERR_PTR() values can be interpreted as internal entries. For example,
ERR_PTR(-EINVAL) has the internal-node bit pattern and may be treated as
an xa_node by xa_load(), causing xas_load() to dereference an invalid
pointer before of_qcom_ice_get() reaches IS_ERR_OR_NULL().

Store probe failures as xa_mk_value() encoded errno values instead, and
convert them back to ERR_PTR() values when loading them from the XArray.

Fixes: d922113ef91e ("soc: qcom: ice: Fix race between qcom_ice_probe() and of_qcom_ice_get()")
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
---
 drivers/soc/qcom/ice.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c
index 5f20108aa03e..bc77dce971a6 100644
--- a/drivers/soc/qcom/ice.c
+++ b/drivers/soc/qcom/ice.c
@@ -667,12 +667,12 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev)
 	}
 
 	ice = xa_load(&ice_handles, pdev->dev.of_node->phandle);
-	if (IS_ERR_OR_NULL(ice)) {
+	if (!ice || xa_is_value(ice)) {
 		platform_device_put(pdev);
 		if (!ice)
 			return ERR_PTR(-EPROBE_DEFER);
 		else
-			return ice;
+			return ERR_PTR(-(long)xa_to_value(ice));
 	}
 
 	link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER);
@@ -744,15 +744,17 @@ static int qcom_ice_probe(struct platform_device *pdev)
 	base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(base)) {
 		dev_warn(&pdev->dev, "ICE registers not found\n");
-		/* Store the error pointer for devm_of_qcom_ice_get() */
-		xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL);
+		/* Store the error code for devm_of_qcom_ice_get() */
+		xa_store(&ice_handles, phandle,
+			 xa_mk_value(-PTR_ERR(base)), GFP_KERNEL);
 		return PTR_ERR(base);
 	}
 
 	engine = qcom_ice_create(&pdev->dev, base);
 	if (IS_ERR(engine)) {
-		/* Store the error pointer for devm_of_qcom_ice_get() */
-		xa_store(&ice_handles, phandle, engine, GFP_KERNEL);
+		/* Store the error code for devm_of_qcom_ice_get() */
+		xa_store(&ice_handles, phandle,
+			 xa_mk_value(-PTR_ERR(engine)), GFP_KERNEL);
 		return PTR_ERR(engine);
 	}
 
-- 
2.43.0