Skip to content

Commit 2efc459

Browse files
JoePerchesgregkh
authored andcommitted
sysfs: Add sysfs_emit and sysfs_emit_at to format sysfs output
Output defects can exist in sysfs content using sprintf and snprintf. sprintf does not know the PAGE_SIZE maximum of the temporary buffer used for outputting sysfs content and it's possible to overrun the PAGE_SIZE buffer length. Add a generic sysfs_emit function that knows that the size of the temporary buffer and ensures that no overrun is done. Add a generic sysfs_emit_at function that can be used in multiple call situations that also ensures that no overrun is done. Validate the output buffer argument to be page aligned. Validate the offset len argument to be within the PAGE_SIZE buf. Signed-off-by: Joe Perches <joe@perches.com> Link: https://lore.kernel.org/r/884235202216d464d61ee975f7465332c86f76b2.1600285923.git.joe@perches.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent e5e5fce commit 2efc459

File tree

3 files changed

+73
-5
lines changed

3 files changed

+73
-5
lines changed

Documentation/filesystems/sysfs.rst

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,10 @@ Other notes:
242242
is 4096.
243243

244244
- show() methods should return the number of bytes printed into the
245-
buffer. This is the return value of scnprintf().
245+
buffer.
246246

247-
- show() must not use snprintf() when formatting the value to be
248-
returned to user space. If you can guarantee that an overflow
249-
will never happen you can use sprintf() otherwise you must use
250-
scnprintf().
247+
- show() should only use sysfs_emit() or sysfs_emit_at() when formatting
248+
the value to be returned to user space.
251249

252250
- store() should return the number of bytes used from the buffer. If the
253251
entire buffer has been used, just return the count argument.

fs/sysfs/file.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/list.h>
1616
#include <linux/mutex.h>
1717
#include <linux/seq_file.h>
18+
#include <linux/mm.h>
1819

1920
#include "sysfs.h"
2021

@@ -707,3 +708,57 @@ int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid)
707708
return 0;
708709
}
709710
EXPORT_SYMBOL_GPL(sysfs_change_owner);
711+
712+
/**
713+
* sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer.
714+
* @buf: start of PAGE_SIZE buffer.
715+
* @fmt: format
716+
* @...: optional arguments to @format
717+
*
718+
*
719+
* Returns number of characters written to @buf.
720+
*/
721+
int sysfs_emit(char *buf, const char *fmt, ...)
722+
{
723+
va_list args;
724+
int len;
725+
726+
if (WARN(!buf || offset_in_page(buf),
727+
"invalid sysfs_emit: buf:%p\n", buf))
728+
return 0;
729+
730+
va_start(args, fmt);
731+
len = vscnprintf(buf, PAGE_SIZE, fmt, args);
732+
va_end(args);
733+
734+
return len;
735+
}
736+
EXPORT_SYMBOL_GPL(sysfs_emit);
737+
738+
/**
739+
* sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer.
740+
* @buf: start of PAGE_SIZE buffer.
741+
* @at: offset in @buf to start write in bytes
742+
* @at must be >= 0 && < PAGE_SIZE
743+
* @fmt: format
744+
* @...: optional arguments to @fmt
745+
*
746+
*
747+
* Returns number of characters written starting at &@buf[@at].
748+
*/
749+
int sysfs_emit_at(char *buf, int at, const char *fmt, ...)
750+
{
751+
va_list args;
752+
int len;
753+
754+
if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE,
755+
"invalid sysfs_emit_at: buf:%p at:%d\n", buf, at))
756+
return 0;
757+
758+
va_start(args, fmt);
759+
len = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args);
760+
va_end(args);
761+
762+
return len;
763+
}
764+
EXPORT_SYMBOL_GPL(sysfs_emit_at);

include/linux/sysfs.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ int sysfs_groups_change_owner(struct kobject *kobj,
329329
int sysfs_group_change_owner(struct kobject *kobj,
330330
const struct attribute_group *groups, kuid_t kuid,
331331
kgid_t kgid);
332+
__printf(2, 3)
333+
int sysfs_emit(char *buf, const char *fmt, ...);
334+
__printf(3, 4)
335+
int sysfs_emit_at(char *buf, int at, const char *fmt, ...);
332336

333337
#else /* CONFIG_SYSFS */
334338

@@ -576,6 +580,17 @@ static inline int sysfs_group_change_owner(struct kobject *kobj,
576580
return 0;
577581
}
578582

583+
__printf(2, 3)
584+
static inline int sysfs_emit(char *buf, const char *fmt, ...)
585+
{
586+
return 0;
587+
}
588+
589+
__printf(3, 4)
590+
static inline int sysfs_emit_at(char *buf, int at, const char *fmt, ...)
591+
{
592+
return 0;
593+
}
579594
#endif /* CONFIG_SYSFS */
580595

581596
static inline int __must_check sysfs_create_file(struct kobject *kobj,

0 commit comments

Comments
 (0)