diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-07 13:24:58 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-07 13:24:58 -0700 | 
| commit | 6ba0d2e4fc2f3e8544f40fb1165c668f78fe951c (patch) | |
| tree | f2b8cc2350bcccb8129e6b1f36b648daa9631c75 | |
| parent | 1fa2c0a0c814fbae0eb3e79a510765225570d043 (diff) | |
| parent | 9af47666cb0f331bfcd76799ee368cdfcb00882c (diff) | |
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
| -rw-r--r-- | kernel/module.c | 22 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/.gitignore | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/Makefile | 4 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/config | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/settings | 1 | ||||
| -rwxr-xr-x | tools/testing/selftests/splice/short_splice_read.sh | 56 | ||||
| -rw-r--r-- | tools/testing/selftests/splice/splice_read.c | 57 | 
7 files changed, 137 insertions, 5 deletions
diff --git a/kernel/module.c b/kernel/module.c index e7b4ff7e4fd0..8fa2600bde6a 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1520,18 +1520,34 @@ struct module_sect_attrs {  	struct module_sect_attr attrs[];  }; +#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))  static ssize_t module_sect_read(struct file *file, struct kobject *kobj,  				struct bin_attribute *battr,  				char *buf, loff_t pos, size_t count)  {  	struct module_sect_attr *sattr =  		container_of(battr, struct module_sect_attr, battr); +	char bounce[MODULE_SECT_READ_SIZE + 1]; +	size_t wrote;  	if (pos != 0)  		return -EINVAL; -	return sprintf(buf, "0x%px\n", -		       kallsyms_show_value(file->f_cred) ? (void *)sattr->address : NULL); +	/* +	 * Since we're a binary read handler, we must account for the +	 * trailing NUL byte that sprintf will write: if "buf" is +	 * too small to hold the NUL, or the NUL is exactly the last +	 * byte, the read will look like it got truncated by one byte. +	 * Since there is no way to ask sprintf nicely to not write +	 * the NUL, we have to use a bounce buffer. +	 */ +	wrote = scnprintf(bounce, sizeof(bounce), "0x%px\n", +			 kallsyms_show_value(file->f_cred) +				? (void *)sattr->address : NULL); +	count = min(count, wrote); +	memcpy(buf, bounce, count); + +	return count;  }  static void free_sect_attrs(struct module_sect_attrs *sect_attrs) @@ -1580,7 +1596,7 @@ static void add_sect_attrs(struct module *mod, const struct load_info *info)  			goto out;  		sect_attrs->nsections++;  		sattr->battr.read = module_sect_read; -		sattr->battr.size = 3 /* "0x", "\n" */ + (BITS_PER_LONG / 4); +		sattr->battr.size = MODULE_SECT_READ_SIZE;  		sattr->battr.attr.mode = 0400;  		*(gattr++) = &(sattr++)->battr;  	} diff --git a/tools/testing/selftests/splice/.gitignore b/tools/testing/selftests/splice/.gitignore index d5a2da428752..be8266f5d04c 100644 --- a/tools/testing/selftests/splice/.gitignore +++ b/tools/testing/selftests/splice/.gitignore @@ -1,2 +1,3 @@  # SPDX-License-Identifier: GPL-2.0-only  default_file_splice_read +splice_read diff --git a/tools/testing/selftests/splice/Makefile b/tools/testing/selftests/splice/Makefile index e519b159b60d..541cd826d5a5 100644 --- a/tools/testing/selftests/splice/Makefile +++ b/tools/testing/selftests/splice/Makefile @@ -1,5 +1,5 @@  # SPDX-License-Identifier: GPL-2.0 -TEST_PROGS := default_file_splice_read.sh -TEST_GEN_PROGS_EXTENDED := default_file_splice_read +TEST_PROGS := default_file_splice_read.sh short_splice_read.sh +TEST_GEN_PROGS_EXTENDED := default_file_splice_read splice_read  include ../lib.mk diff --git a/tools/testing/selftests/splice/config b/tools/testing/selftests/splice/config new file mode 100644 index 000000000000..058c928368b8 --- /dev/null +++ b/tools/testing/selftests/splice/config @@ -0,0 +1 @@ +CONFIG_TEST_LKM=m diff --git a/tools/testing/selftests/splice/settings b/tools/testing/selftests/splice/settings new file mode 100644 index 000000000000..89cedfc0d12b --- /dev/null +++ b/tools/testing/selftests/splice/settings @@ -0,0 +1 @@ +timeout=5 diff --git a/tools/testing/selftests/splice/short_splice_read.sh b/tools/testing/selftests/splice/short_splice_read.sh new file mode 100755 index 000000000000..7810d3589d9a --- /dev/null +++ b/tools/testing/selftests/splice/short_splice_read.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +set -e + +ret=0 + +do_splice() +{ +	filename="$1" +	bytes="$2" +	expected="$3" + +	out=$(./splice_read "$filename" "$bytes" | cat) +	if [ "$out" = "$expected" ] ; then +		echo "ok: $filename $bytes" +	else +		echo "FAIL: $filename $bytes" +		ret=1 +	fi +} + +test_splice() +{ +	filename="$1" + +	full=$(cat "$filename") +	two=$(echo "$full" | grep -m1 . | cut -c-2) + +	# Make sure full splice has the same contents as a standard read. +	do_splice "$filename" 4096 "$full" + +	# Make sure a partial splice see the first two characters. +	do_splice "$filename" 2 "$two" +} + +# proc_single_open(), seq_read() +test_splice /proc/$$/limits +# special open, seq_read() +test_splice /proc/$$/comm + +# proc_handler, proc_dointvec_minmax +test_splice /proc/sys/fs/nr_open +# proc_handler, proc_dostring +test_splice /proc/sys/kernel/modprobe +# proc_handler, special read +test_splice /proc/sys/kernel/version + +if ! [ -d /sys/module/test_module/sections ] ; then +	modprobe test_module +fi +# kernfs, attr +test_splice /sys/module/test_module/coresize +# kernfs, binattr +test_splice /sys/module/test_module/sections/.init.text + +exit $ret diff --git a/tools/testing/selftests/splice/splice_read.c b/tools/testing/selftests/splice/splice_read.c new file mode 100644 index 000000000000..46dae6a25cfb --- /dev/null +++ b/tools/testing/selftests/splice/splice_read.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +int main(int argc, char *argv[]) +{ +	int fd; +	size_t size; +	ssize_t spliced; + +	if (argc < 2) { +		fprintf(stderr, "Usage: %s INPUT [BYTES]\n", argv[0]); +		return EXIT_FAILURE; +	} + +	fd = open(argv[1], O_RDONLY); +	if (fd < 0) { +		perror(argv[1]); +		return EXIT_FAILURE; +	} + +	if (argc == 3) +		size = atol(argv[2]); +	else { +		struct stat statbuf; + +		if (fstat(fd, &statbuf) < 0) { +			perror(argv[1]); +			return EXIT_FAILURE; +		} + +		if (statbuf.st_size > INT_MAX) { +			fprintf(stderr, "%s: Too big\n", argv[1]); +			return EXIT_FAILURE; +		} + +		size = statbuf.st_size; +	} + +	/* splice(2) file to stdout. */ +	spliced = splice(fd, NULL, STDOUT_FILENO, NULL, +		      size, SPLICE_F_MOVE); +	if (spliced < 0) { +		perror("splice"); +		return EXIT_FAILURE; +	} + +	close(fd); +	return EXIT_SUCCESS; +}  | 
