From: Haruka Kuze harukak@zohomail.eu
OP-TEE has a reference of RPMB device it used. So, it can route frames through the device after rpmb_dev_unregister() is called. Therefore, the parent device passed to route_frames() can be already freed, for example, mmc_blk_remove_rpmb_part() drops the last reference after rpmb unregister.
This patch clears the route_frames when the device is going to be removed by getting new rwsem and makes rpmb_route_frames() return -ENODEV if the device is gone.
Fixes: 1e9046e3a154 ("rpmb: add Replay Protected Memory Block (RPMB) subsystem") Assisted-by: LLM Signed-off-by: Haruka Kuze harukak@zohomail.eu --- drivers/misc/rpmb-core.c | 14 +++++++++++++- include/linux/rpmb.h | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/misc/rpmb-core.c b/drivers/misc/rpmb-core.c index ecf14acf230a..0ae018d1a276 100644 --- a/drivers/misc/rpmb-core.c +++ b/drivers/misc/rpmb-core.c @@ -45,7 +45,7 @@ EXPORT_SYMBOL_GPL(rpmb_dev_put); * @rsp: rpmb response frames * @rsp_len: length of rpmb response frames in bytes * - * Returns: < 0 on failure + * Returns: < 0 on failure, -ENODEV if the device has been unregistered */ int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, unsigned int req_len, u8 *rsp, unsigned int rsp_len) @@ -53,6 +53,10 @@ int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, if (!req || !req_len || !rsp || !rsp_len) return -EINVAL;
+ guard(rwsem_read)(&rdev->lock); + if (!rdev->descr.route_frames) + return -ENODEV; + return rdev->descr.route_frames(rdev->dev.parent, req, req_len, rsp, rsp_len); } @@ -126,6 +130,10 @@ EXPORT_SYMBOL_GPL(rpmb_interface_unregister); * This function should be called from the release function of the * underlying device used when the RPMB device was registered. * + * Once this returns, rpmb_route_frames() no longer calls into + * the driver, even though the users might still hold a reference + * to the RPMB dev. + * * Returns: < 0 on failure */ int rpmb_dev_unregister(struct rpmb_dev *rdev) @@ -133,6 +141,9 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev) if (!rdev) return -EINVAL;
+ scoped_guard(rwsem_write, &rdev->lock) + rdev->descr.route_frames = NULL; + device_del(&rdev->dev);
rpmb_dev_put(rdev); @@ -165,6 +176,7 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, if (!rdev) return ERR_PTR(-ENOMEM); rdev->descr = *descr; + init_rwsem(&rdev->lock); rdev->descr.dev_id = kmemdup(descr->dev_id, descr->dev_id_len, GFP_KERNEL); if (!rdev->descr.dev_id) { diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h index ed3f8e431eff..0a976572d1cd 100644 --- a/include/linux/rpmb.h +++ b/include/linux/rpmb.h @@ -7,6 +7,7 @@ #define __RPMB_H__
#include <linux/device.h> +#include <linux/rwsem.h> #include <linux/types.h>
/** @@ -51,12 +52,15 @@ struct rpmb_descr { * @id : device_id * @list_node : linked list node * @descr : RPMB description + * @lock : protect @descr.route_frames which is cleared when this + * device is unregistered */ struct rpmb_dev { struct device dev; int id; struct list_head list_node; struct rpmb_descr descr; + struct rw_semaphore lock; };
#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev)