Add a helper, sysfs_strscpy(), which simply copies a string and
appends a newline and NUL char to the end, making sure not to overflow
the destination buffer, which MUST be PAGE_SIZE bytes (which is true for
buffers in this context). It includes a compile time check for the
specified destination buffer size.

Also add a helper macro, sysfs_strcpy(), which calls sysfs_strscpy() with
count == PAGE_SIZE, for use with regular NUL-terminated strings. If the
src buffer is a fixed-size array, guarantee that we don't copy beyond its
end by only copying a maximum of sizeof(src) bytes.

Signed-off-by: Alex Dewar <alex.dewa...@gmail.com>
---
 fs/sysfs/file.c       | 14 ++++++++++++++
 include/linux/sysfs.h | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index eb6897ab78e7..2a60e5c6392d 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -707,3 +707,17 @@ int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, 
kgid_t kgid)
        return 0;
 }
 EXPORT_SYMBOL_GPL(sysfs_change_owner);
+
+ssize_t __sysfs_strscpy(char *dest, const char *src, size_t count)
+{
+       ssize_t written;
+
+       if (count > PAGE_SIZE)
+               return -EINVAL;
+
+       written = strscpy(dest, src, count - 1);
+       dest[written++] = '\n';
+       dest[written] = '\0';
+       return written;
+}
+EXPORT_SYMBOL_GPL(__sysfs_strscpy);
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 34e84122f635..26e7d9f69dfd 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -162,6 +162,41 @@ static const struct attribute_group _name##_group = {      
        \
 };                                                             \
 __ATTRIBUTE_GROUPS(_name)
 
+/**
+ *     sysfs_strscpy - return a string from a show method with terminating
+ *     newline to a maximum of count bytes
+ *     @dest: destination buffer
+ *     @src: string to be emitted
+ *     @count: maximum number of bytes to be written to dest
+ */
+#define sysfs_strscpy(dest, src, count)                                       \
+({                                                                            \
+       BUILD_BUG_ON(__builtin_constant_p(count) && (count) > PAGE_SIZE);      \
+       __sysfs_strscpy((dest), (src), (count));                               \
+})
+
+ssize_t __sysfs_strscpy(char *dest, const char *src, size_t count);
+
+/**
+ *     sysfs_strcpy - return a string from a show method with terminating
+ *     newline
+ *     @dest: destination buffer
+ *     @src: string to be emitted
+ *
+ *     This method will only write a maximum of PAGE_SIZE bytes to dest,
+ *     ensuring that the output buffer is not overflown. If src is a
+ *     fixed-size array, a maximum of sizeof(src) bytes will be copied,
+ *     ensuring that memory is not read beyond the end of the array.
+ */
+#define sysfs_strcpy(dest, src)                                      \
+sysfs_strscpy((dest), (src),                                         \
+               __builtin_choose_expr(                                \
+                       __same_type((src), &(src)[0]),                \
+                       PAGE_SIZE,                                    \
+                       min(sizeof(src) + 2, PAGE_SIZE)               \
+               )                                                     \
+)
+
 struct file;
 struct vm_area_struct;
 
-- 
2.28.0

Reply via email to