Skip to content

Commit 42023d4

Browse files
2045geminimathieupoirier
authored andcommitted
rpmsg: core: fix race in driver_override_show() and use core helper
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: 39e4776 ("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>
1 parent c38d8b6 commit 42023d4

1 file changed

Lines changed: 27 additions & 39 deletions

File tree

drivers/rpmsg/rpmsg_core.c

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -352,50 +352,38 @@ field##_show(struct device *dev, \
352352
} \
353353
static DEVICE_ATTR_RO(field);
354354

355-
#define rpmsg_string_attr(field, member) \
356-
static ssize_t \
357-
field##_store(struct device *dev, struct device_attribute *attr, \
358-
const char *buf, size_t sz) \
359-
{ \
360-
struct rpmsg_device *rpdev = to_rpmsg_device(dev); \
361-
const char *old; \
362-
char *new; \
363-
\
364-
new = kstrndup(buf, sz, GFP_KERNEL); \
365-
if (!new) \
366-
return -ENOMEM; \
367-
new[strcspn(new, "\n")] = '\0'; \
368-
\
369-
device_lock(dev); \
370-
old = rpdev->member; \
371-
if (strlen(new)) { \
372-
rpdev->member = new; \
373-
} else { \
374-
kfree(new); \
375-
rpdev->member = NULL; \
376-
} \
377-
device_unlock(dev); \
378-
\
379-
kfree(old); \
380-
\
381-
return sz; \
382-
} \
383-
static ssize_t \
384-
field##_show(struct device *dev, \
385-
struct device_attribute *attr, char *buf) \
386-
{ \
387-
struct rpmsg_device *rpdev = to_rpmsg_device(dev); \
388-
\
389-
return sprintf(buf, "%s\n", rpdev->member); \
390-
} \
391-
static DEVICE_ATTR_RW(field)
392-
393355
/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
394356
rpmsg_show_attr(name, id.name, "%s\n");
395357
rpmsg_show_attr(src, src, "0x%x\n");
396358
rpmsg_show_attr(dst, dst, "0x%x\n");
397359
rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
398-
rpmsg_string_attr(driver_override, driver_override);
360+
361+
static ssize_t driver_override_store(struct device *dev,
362+
struct device_attribute *attr,
363+
const char *buf, size_t count)
364+
{
365+
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
366+
int ret;
367+
368+
ret = driver_set_override(dev, &rpdev->driver_override, buf, count);
369+
if (ret)
370+
return ret;
371+
372+
return count;
373+
}
374+
375+
static ssize_t driver_override_show(struct device *dev,
376+
struct device_attribute *attr, char *buf)
377+
{
378+
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
379+
ssize_t len;
380+
381+
device_lock(dev);
382+
len = sysfs_emit(buf, "%s\n", rpdev->driver_override);
383+
device_unlock(dev);
384+
return len;
385+
}
386+
static DEVICE_ATTR_RW(driver_override);
399387

400388
static ssize_t modalias_show(struct device *dev,
401389
struct device_attribute *attr, char *buf)

0 commit comments

Comments
 (0)