]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
rpmsg: core: fix race in driver_override_show() and use core helper
authorGui-Dong Han <hanguidong02@gmail.com>
Tue, 2 Dec 2025 17:49:48 +0000 (01:49 +0800)
committerMathieu Poirier <mathieu.poirier@linaro.org>
Mon, 15 Dec 2025 01:37:49 +0000 (18:37 -0700)
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: stable@vger.kernel.org
Signed-off-by: Gui-Dong Han <hanguidong02@gmail.com>
Link: https://lore.kernel.org/r/20251202174948.12693-1-hanguidong02@gmail.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
drivers/rpmsg/rpmsg_core.c

index 5d661681a9b6ce6f60edf79035ef2e339b2b511c..96964745065b1fbdd46442c040e55c3680181736 100644 (file)
@@ -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)