Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Joe Perches and committed by
Greg Kroah-Hartman
2efc459d e5e5fcef

+73 -5
+3 -5
Documentation/filesystems/sysfs.rst
··· 242 242 is 4096. 243 243 244 244 - show() methods should return the number of bytes printed into the 245 - buffer. This is the return value of scnprintf(). 245 + buffer. 246 246 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. 251 249 252 250 - store() should return the number of bytes used from the buffer. If the 253 251 entire buffer has been used, just return the count argument.
+55
fs/sysfs/file.c
··· 15 15 #include <linux/list.h> 16 16 #include <linux/mutex.h> 17 17 #include <linux/seq_file.h> 18 + #include <linux/mm.h> 18 19 19 20 #include "sysfs.h" 20 21 ··· 708 707 return 0; 709 708 } 710 709 EXPORT_SYMBOL_GPL(sysfs_change_owner); 710 + 711 + /** 712 + * sysfs_emit - scnprintf equivalent, aware of PAGE_SIZE buffer. 713 + * @buf: start of PAGE_SIZE buffer. 714 + * @fmt: format 715 + * @...: optional arguments to @format 716 + * 717 + * 718 + * Returns number of characters written to @buf. 719 + */ 720 + int sysfs_emit(char *buf, const char *fmt, ...) 721 + { 722 + va_list args; 723 + int len; 724 + 725 + if (WARN(!buf || offset_in_page(buf), 726 + "invalid sysfs_emit: buf:%p\n", buf)) 727 + return 0; 728 + 729 + va_start(args, fmt); 730 + len = vscnprintf(buf, PAGE_SIZE, fmt, args); 731 + va_end(args); 732 + 733 + return len; 734 + } 735 + EXPORT_SYMBOL_GPL(sysfs_emit); 736 + 737 + /** 738 + * sysfs_emit_at - scnprintf equivalent, aware of PAGE_SIZE buffer. 739 + * @buf: start of PAGE_SIZE buffer. 740 + * @at: offset in @buf to start write in bytes 741 + * @at must be >= 0 && < PAGE_SIZE 742 + * @fmt: format 743 + * @...: optional arguments to @fmt 744 + * 745 + * 746 + * Returns number of characters written starting at &@buf[@at]. 747 + */ 748 + int sysfs_emit_at(char *buf, int at, const char *fmt, ...) 749 + { 750 + va_list args; 751 + int len; 752 + 753 + if (WARN(!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE, 754 + "invalid sysfs_emit_at: buf:%p at:%d\n", buf, at)) 755 + return 0; 756 + 757 + va_start(args, fmt); 758 + len = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args); 759 + va_end(args); 760 + 761 + return len; 762 + } 763 + EXPORT_SYMBOL_GPL(sysfs_emit_at);
+15
include/linux/sysfs.h
··· 329 329 int sysfs_group_change_owner(struct kobject *kobj, 330 330 const struct attribute_group *groups, kuid_t kuid, 331 331 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, ...); 332 336 333 337 #else /* CONFIG_SYSFS */ 334 338 ··· 580 576 return 0; 581 577 } 582 578 579 + __printf(2, 3) 580 + static inline int sysfs_emit(char *buf, const char *fmt, ...) 581 + { 582 + return 0; 583 + } 584 + 585 + __printf(3, 4) 586 + static inline int sysfs_emit_at(char *buf, int at, const char *fmt, ...) 587 + { 588 + return 0; 589 + } 583 590 #endif /* CONFIG_SYSFS */ 584 591 585 592 static inline int __must_check sysfs_create_file(struct kobject *kobj,