Petr Mladek c5a70d
From 2efc459d06f1630001e3984854848a5647086232 Mon Sep 17 00:00:00 2001
Petr Mladek c5a70d
From: Joe Perches <joe@perches.com>
Petr Mladek c5a70d
Date: Wed, 16 Sep 2020 13:40:38 -0700
Petr Mladek c5a70d
Subject: [PATCH] sysfs: Add sysfs_emit and sysfs_emit_at to format sysfs
Petr Mladek c5a70d
 output
Petr Mladek c5a70d
Git-commit: 2efc459d06f1630001e3984854848a5647086232
Petr Mladek c5a70d
Patch-mainline: v5.10-rc1
Petr Mladek c5a70d
References: bsc#1200598 CVE-2022-20166
Petr Mladek c5a70d
Petr Mladek c5a70d
Output defects can exist in sysfs content using sprintf and snprintf.
Petr Mladek c5a70d
Petr Mladek c5a70d
sprintf does not know the PAGE_SIZE maximum of the temporary buffer
Petr Mladek c5a70d
used for outputting sysfs content and it's possible to overrun the
Petr Mladek c5a70d
PAGE_SIZE buffer length.
Petr Mladek c5a70d
Petr Mladek c5a70d
Add a generic sysfs_emit function that knows that the size of the
Petr Mladek c5a70d
temporary buffer and ensures that no overrun is done.
Petr Mladek c5a70d
Petr Mladek c5a70d
Add a generic sysfs_emit_at function that can be used in multiple
Petr Mladek c5a70d
call situations that also ensures that no overrun is done.
Petr Mladek c5a70d
Petr Mladek c5a70d
Validate the output buffer argument to be page aligned.
Petr Mladek c5a70d
Validate the offset len argument to be within the PAGE_SIZE buf.
Petr Mladek c5a70d
Petr Mladek c5a70d
Signed-off-by: Joe Perches <joe@perches.com>
Petr Mladek c5a70d
Link: https://lore.kernel.org/r/884235202216d464d61ee975f7465332c86f76b2.1600285923.git.joe@perches.com
Petr Mladek c5a70d
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Petr Mladek c5a70d
Acked-by: Petr Mladek <pmladek@suse.com>
Petr Mladek c5a70d
Petr Mladek c5a70d
[pmladek@suse.com: Removed changes in the documentation. The new API is not
Petr Mladek c5a70d
	used everywhere in the old code base.
Petr Mladek c5a70d
]
Petr Mladek c5a70d
Petr Mladek c5a70d
---
Petr Mladek c5a70d
 fs/sysfs/file.c       |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++
Petr Mladek c5a70d
 include/linux/sysfs.h |   16 ++++++++++++++
Petr Mladek c5a70d
 2 files changed, 71 insertions(+)
Petr Mladek c5a70d
Petr Mladek c5a70d
--- a/fs/sysfs/file.c
Petr Mladek c5a70d
+++ b/fs/sysfs/file.c
Petr Mladek c5a70d
@@ -17,6 +17,7 @@
Petr Mladek c5a70d
 #include <linux/list.h>
Petr Mladek c5a70d
 #include <linux/mutex.h>
Petr Mladek c5a70d
 #include <linux/seq_file.h>
Petr Mladek c5a70d
+#include <linux/mm.h>
Petr Mladek c5a70d
 
Petr Mladek c5a70d
 #include "sysfs.h"
Petr Mladek c5a70d
 #include "../kernfs/kernfs-internal.h"
Petr Mladek c5a70d
@@ -549,3 +550,57 @@ void sysfs_remove_bin_file(struct kobjec
Petr Mladek c5a70d
 	kernfs_remove_by_name(kobj->sd, attr->attr.name);
Petr Mladek c5a70d
 }
Petr Mladek c5a70d
 EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+/**
Petr Mladek c5a70d
+ *	sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer.
Petr Mladek c5a70d
+ *	@buf:	start of PAGE_SIZE buffer.
Petr Mladek c5a70d
+ *	@fmt:	format
Petr Mladek c5a70d
+ *	@...:	optional arguments to @format
Petr Mladek c5a70d
+ *
Petr Mladek c5a70d
+ *
Petr Mladek c5a70d
+ * Returns number of characters written to @buf.
Petr Mladek c5a70d
+ */
Petr Mladek c5a70d
+int sysfs_emit(char *buf, const char *fmt, ...)
Petr Mladek c5a70d
+{
Petr Mladek c5a70d
+	va_list args;
Petr Mladek c5a70d
+	int len;
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+	if (WARN(!buf || offset_in_page(buf),
Petr Mladek c5a70d
+		 "invalid sysfs_emit: buf:%p\n", buf))
Petr Mladek c5a70d
+		return 0;
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+	va_start(args, fmt);
Petr Mladek c5a70d
+	len = vscnprintf(buf, PAGE_SIZE, fmt, args);
Petr Mladek c5a70d
+	va_end(args);
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+	return len;
Petr Mladek c5a70d
+}
Petr Mladek c5a70d
+EXPORT_SYMBOL_GPL(sysfs_emit);
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+/**
Petr Mladek c5a70d
+ *	sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer.
Petr Mladek c5a70d
+ *	@buf:	start of PAGE_SIZE buffer.
Petr Mladek c5a70d
+ *	@at:	offset in @buf to start write in bytes
Petr Mladek c5a70d
+ *		@at must be >= 0 && < PAGE_SIZE
Petr Mladek c5a70d
+ *	@fmt:	format
Petr Mladek c5a70d
+ *	@...:	optional arguments to @fmt
Petr Mladek c5a70d
+ *
Petr Mladek c5a70d
+ *
Petr Mladek c5a70d
+ * Returns number of characters written starting at &@buf[@at].
Petr Mladek c5a70d
+ */
Petr Mladek c5a70d
+int sysfs_emit_at(char *buf, int at, const char *fmt, ...)
Petr Mladek c5a70d
+{
Petr Mladek c5a70d
+	va_list args;
Petr Mladek c5a70d
+	int len;
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+	if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE,
Petr Mladek c5a70d
+		 "invalid sysfs_emit_at: buf:%p at:%d\n", buf, at))
Petr Mladek c5a70d
+		return 0;
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+	va_start(args, fmt);
Petr Mladek c5a70d
+	len = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args);
Petr Mladek c5a70d
+	va_end(args);
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+	return len;
Petr Mladek c5a70d
+}
Petr Mladek c5a70d
+EXPORT_SYMBOL_GPL(sysfs_emit_at);
Petr Mladek c5a70d
--- a/include/linux/sysfs.h
Petr Mladek c5a70d
+++ b/include/linux/sysfs.h
Petr Mladek c5a70d
@@ -300,6 +300,11 @@ static inline void sysfs_enable_ns(struc
Petr Mladek c5a70d
 	return kernfs_enable_ns(kn);
Petr Mladek c5a70d
 }
Petr Mladek c5a70d
 
Petr Mladek c5a70d
+__printf(2, 3)
Petr Mladek c5a70d
+int sysfs_emit(char *buf, const char *fmt, ...);
Petr Mladek c5a70d
+__printf(3, 4)
Petr Mladek c5a70d
+int sysfs_emit_at(char *buf, int at, const char *fmt, ...);
Petr Mladek c5a70d
+
Petr Mladek c5a70d
 #else /* CONFIG_SYSFS */
Petr Mladek c5a70d
 
Petr Mladek c5a70d
 static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
Petr Mladek c5a70d
@@ -506,6 +511,17 @@ static inline void sysfs_enable_ns(struc
Petr Mladek c5a70d
 {
Petr Mladek c5a70d
 }
Petr Mladek c5a70d
 
Petr Mladek c5a70d
+__printf(2, 3)
Petr Mladek c5a70d
+static inline int sysfs_emit(char *buf, const char *fmt, ...)
Petr Mladek c5a70d
+{
Petr Mladek c5a70d
+	return 0;
Petr Mladek c5a70d
+}
Petr Mladek c5a70d
+
Petr Mladek c5a70d
+__printf(3, 4)
Petr Mladek c5a70d
+static inline int sysfs_emit_at(char *buf, int at, const char *fmt, ...)
Petr Mladek c5a70d
+{
Petr Mladek c5a70d
+	return 0;
Petr Mladek c5a70d
+}
Petr Mladek c5a70d
 #endif /* CONFIG_SYSFS */
Petr Mladek c5a70d
 
Petr Mladek c5a70d
 static inline int __must_check sysfs_create_file(struct kobject *kobj,