The driver_override_show function reads the driver_override string
without holding the device_lock. However, the store function modifies
and frees the string while holding the device_lock. This creates a race
condition where the string can be freed by the store function while
being read by the show function, leading to a use-after-free.

To fix this, replace the rpmsg_string_attr macro with explicit show and
store functions. The new driver_override_store uses the standard
driver_set_override helper. Since the introduction of
driver_set_override, the comments in include/linux/rpmsg.h have stated
that this helper must be used to set or clear driver_override, but the
implementation was not updated until now.

Because driver_set_override modifies and frees the string while holding
the device_lock, the new driver_override_show now correctly holds the
device_lock during the read operation to prevent the race.

Additionally, since rpmsg_string_attr has only ever been used for
driver_override, removing the macro simplifies the code.

Fixes: 39e47767ec9b ("rpmsg: Add driver_override device attribute for 
rpmsg_device")
Cc: [email protected]
Signed-off-by: Gui-Dong Han <[email protected]>
---
I verified this with a stress test that continuously writes/reads the
attribute. It triggered KASAN and leaked bytes like a0 f4 81 9f a3 ff ff
(likely kernel pointers). Since driver_override is world-readable (0644),
this allows unprivileged users to leak kernel pointers and bypass KASLR.
Similar races were fixed in other buses (e.g., commits 9561475db680 and
91d44c1afc61). Currently, 9 of 11 buses handle this correctly; this patch
fixes one of the remaining two.
---
 drivers/rpmsg/rpmsg_core.c | 66 ++++++++++++++++----------------------
 1 file changed, 27 insertions(+), 39 deletions(-)

diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c
index 5d661681a9b6..96964745065b 100644
--- a/drivers/rpmsg/rpmsg_core.c
+++ b/drivers/rpmsg/rpmsg_core.c
@@ -352,50 +352,38 @@ field##_show(struct device *dev,                          
        \
 }                                                                      \
 static DEVICE_ATTR_RO(field);
 
-#define rpmsg_string_attr(field, member)                               \
-static ssize_t                                                         \
-field##_store(struct device *dev, struct device_attribute *attr,       \
-             const char *buf, size_t sz)                               \
-{                                                                      \
-       struct rpmsg_device *rpdev = to_rpmsg_device(dev);              \
-       const char *old;                                                \
-       char *new;                                                      \
-                                                                       \
-       new = kstrndup(buf, sz, GFP_KERNEL);                            \
-       if (!new)                                                       \
-               return -ENOMEM;                                         \
-       new[strcspn(new, "\n")] = '\0';                                 \
-                                                                       \
-       device_lock(dev);                                               \
-       old = rpdev->member;                                            \
-       if (strlen(new)) {                                              \
-               rpdev->member = new;                                    \
-       } else {                                                        \
-               kfree(new);                                             \
-               rpdev->member = NULL;                                   \
-       }                                                               \
-       device_unlock(dev);                                             \
-                                                                       \
-       kfree(old);                                                     \
-                                                                       \
-       return sz;                                                      \
-}                                                                      \
-static ssize_t                                                         \
-field##_show(struct device *dev,                                       \
-            struct device_attribute *attr, char *buf)                  \
-{                                                                      \
-       struct rpmsg_device *rpdev = to_rpmsg_device(dev);              \
-                                                                       \
-       return sprintf(buf, "%s\n", rpdev->member);                     \
-}                                                                      \
-static DEVICE_ATTR_RW(field)
-
 /* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
 rpmsg_show_attr(name, id.name, "%s\n");
 rpmsg_show_attr(src, src, "0x%x\n");
 rpmsg_show_attr(dst, dst, "0x%x\n");
 rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
-rpmsg_string_attr(driver_override, driver_override);
+
+static ssize_t driver_override_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct rpmsg_device *rpdev = to_rpmsg_device(dev);
+       int ret;
+
+       ret = driver_set_override(dev, &rpdev->driver_override, buf, count);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static ssize_t driver_override_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct rpmsg_device *rpdev = to_rpmsg_device(dev);
+       ssize_t len;
+
+       device_lock(dev);
+       len = sysfs_emit(buf, "%s\n", rpdev->driver_override);
+       device_unlock(dev);
+       return len;
+}
+static DEVICE_ATTR_RW(driver_override);
 
 static ssize_t modalias_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
-- 
2.43.0


Reply via email to