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

Merge tag 'kallsyms_show_value-fix-v5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull sysfs module section fix from Kees Cook:
"Fix sysfs module section output overflow.

About a month after my kallsyms_show_value() refactoring landed, 0day
noticed that there was a path through the kernfs binattr read handlers
that did not have PAGE_SIZEd buffers, and the module "sections" read
handler made a bad assumption about this, resulting in it stomping on
memory when reached through small-sized splice() calls.

I've added a set of tests to find these kinds of regressions more
quickly in the future as well"

Sefltests-acked-by: Shuah Khan <skhan@linuxfoundation.org>

* tag 'kallsyms_show_value-fix-v5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
selftests: splice: Check behavior of full and short splices
module: Correctly truncate sysfs sections output

+137 -5
+19 -3
kernel/module.c
··· 1520 1520 struct module_sect_attr attrs[]; 1521 1521 }; 1522 1522 1523 + #define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4)) 1523 1524 static ssize_t module_sect_read(struct file *file, struct kobject *kobj, 1524 1525 struct bin_attribute *battr, 1525 1526 char *buf, loff_t pos, size_t count) 1526 1527 { 1527 1528 struct module_sect_attr *sattr = 1528 1529 container_of(battr, struct module_sect_attr, battr); 1530 + char bounce[MODULE_SECT_READ_SIZE + 1]; 1531 + size_t wrote; 1529 1532 1530 1533 if (pos != 0) 1531 1534 return -EINVAL; 1532 1535 1533 - return sprintf(buf, "0x%px\n", 1534 - kallsyms_show_value(file->f_cred) ? (void *)sattr->address : NULL); 1536 + /* 1537 + * Since we're a binary read handler, we must account for the 1538 + * trailing NUL byte that sprintf will write: if "buf" is 1539 + * too small to hold the NUL, or the NUL is exactly the last 1540 + * byte, the read will look like it got truncated by one byte. 1541 + * Since there is no way to ask sprintf nicely to not write 1542 + * the NUL, we have to use a bounce buffer. 1543 + */ 1544 + wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n", 1545 + kallsyms_show_value(file->f_cred) 1546 + ? (void *)sattr->address : NULL); 1547 + count = min(count, wrote); 1548 + memcpy(buf, bounce, count); 1549 + 1550 + return count; 1535 1551 } 1536 1552 1537 1553 static void free_sect_attrs(struct module_sect_attrs *sect_attrs) ··· 1596 1580 goto out; 1597 1581 sect_attrs->nsections++; 1598 1582 sattr->battr.read = module_sect_read; 1599 - sattr->battr.size = 3 /* "0x", "\n" */ + (BITS_PER_LONG / 4); 1583 + sattr->battr.size = MODULE_SECT_READ_SIZE; 1600 1584 sattr->battr.attr.mode = 0400; 1601 1585 *(gattr++) = &(sattr++)->battr; 1602 1586 }
+1
tools/testing/selftests/splice/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 default_file_splice_read 3 + splice_read
+2 -2
tools/testing/selftests/splice/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 - TEST_PROGS := default_file_splice_read.sh 3 - TEST_GEN_PROGS_EXTENDED := default_file_splice_read 2 + TEST_PROGS := default_file_splice_read.sh short_splice_read.sh 3 + TEST_GEN_PROGS_EXTENDED := default_file_splice_read splice_read 4 4 5 5 include ../lib.mk
+1
tools/testing/selftests/splice/config
··· 1 + CONFIG_TEST_LKM=m
+1
tools/testing/selftests/splice/settings
··· 1 + timeout=5
+56
tools/testing/selftests/splice/short_splice_read.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + set -e 4 + 5 + ret=0 6 + 7 + do_splice() 8 + { 9 + filename="$1" 10 + bytes="$2" 11 + expected="$3" 12 + 13 + out=$(./splice_read "$filename" "$bytes" | cat) 14 + if [ "$out" = "$expected" ] ; then 15 + echo "ok: $filename $bytes" 16 + else 17 + echo "FAIL: $filename $bytes" 18 + ret=1 19 + fi 20 + } 21 + 22 + test_splice() 23 + { 24 + filename="$1" 25 + 26 + full=$(cat "$filename") 27 + two=$(echo "$full" | grep -m1 . | cut -c-2) 28 + 29 + # Make sure full splice has the same contents as a standard read. 30 + do_splice "$filename" 4096 "$full" 31 + 32 + # Make sure a partial splice see the first two characters. 33 + do_splice "$filename" 2 "$two" 34 + } 35 + 36 + # proc_single_open(), seq_read() 37 + test_splice /proc/$$/limits 38 + # special open, seq_read() 39 + test_splice /proc/$$/comm 40 + 41 + # proc_handler, proc_dointvec_minmax 42 + test_splice /proc/sys/fs/nr_open 43 + # proc_handler, proc_dostring 44 + test_splice /proc/sys/kernel/modprobe 45 + # proc_handler, special read 46 + test_splice /proc/sys/kernel/version 47 + 48 + if ! [ -d /sys/module/test_module/sections ] ; then 49 + modprobe test_module 50 + fi 51 + # kernfs, attr 52 + test_splice /sys/module/test_module/coresize 53 + # kernfs, binattr 54 + test_splice /sys/module/test_module/sections/.init.text 55 + 56 + exit $ret
+57
tools/testing/selftests/splice/splice_read.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define _GNU_SOURCE 3 + #include <errno.h> 4 + #include <fcntl.h> 5 + #include <limits.h> 6 + #include <stdio.h> 7 + #include <stdlib.h> 8 + #include <unistd.h> 9 + #include <sys/types.h> 10 + #include <sys/stat.h> 11 + 12 + int main(int argc, char *argv[]) 13 + { 14 + int fd; 15 + size_t size; 16 + ssize_t spliced; 17 + 18 + if (argc < 2) { 19 + fprintf(stderr, "Usage: %s INPUT [BYTES]\n", argv[0]); 20 + return EXIT_FAILURE; 21 + } 22 + 23 + fd = open(argv[1], O_RDONLY); 24 + if (fd < 0) { 25 + perror(argv[1]); 26 + return EXIT_FAILURE; 27 + } 28 + 29 + if (argc == 3) 30 + size = atol(argv[2]); 31 + else { 32 + struct stat statbuf; 33 + 34 + if (fstat(fd, &statbuf) < 0) { 35 + perror(argv[1]); 36 + return EXIT_FAILURE; 37 + } 38 + 39 + if (statbuf.st_size > INT_MAX) { 40 + fprintf(stderr, "%s: Too big\n", argv[1]); 41 + return EXIT_FAILURE; 42 + } 43 + 44 + size = statbuf.st_size; 45 + } 46 + 47 + /* splice(2) file to stdout. */ 48 + spliced = splice(fd, NULL, STDOUT_FILENO, NULL, 49 + size, SPLICE_F_MOVE); 50 + if (spliced < 0) { 51 + perror("splice"); 52 + return EXIT_FAILURE; 53 + } 54 + 55 + close(fd); 56 + return EXIT_SUCCESS; 57 + }