Fix sysfs module section output overflow
-----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAl8tsE4WHGtlZXNjb29r QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJhw+D/9nB8+KxD2yYp2ntoLrhu8cUP6V LF8C7eQwFI/SV/Z/5ZpQPpBbndJAPz1ob/kZ8v5N4+EGfr3eRyI76RWnshl/CpA1 X/sYCSHezer52giAC59RGt0Nc/S6/sUrVU6/b28tzhoTYxJ6SoDl4WgC2pGGTPdY ei/KeMPtH2lpy3NazCmLwIAElgnXBDrJZYtuaaIOe/WPDbJ+cbRJzsJ9VGItXqNc h9n8vpExgHd7ThkM1xlJ5q7Q5KFltKUxGZJoOciLPNJshJ1o0NTMeo/7i8TF3aZZ aVglnYVI/SKbrEa2JhboM4M7ytfAL606xYPsHr57ojBqxdhUk5zhFOi5uKyaM6Gm t6wX9o5jfFCg3AZhyd+IP3q7Zc9z1IWMGjwFrNznchwvz2eCcSytOxOkIMuo9o2T cs79++kmczAit9z9LmMGpHfHWFBOX3gvzfkMqBZMD4+6EeZ33U1CCnkMZuqmajqf MYZzLzVibrcb6cUuZZm+lmhVgoBrr/HPy6BNf5s8n39PJGMbwkAqHACZI7+78VHu vVcezubF0IyswRFJGcS19HVWOVJ2lNux8FUnEIOEtxIaUYsSYbwQZnWyFiwxOHJ9 +wZpcgMVLpEXCtOyhvgecn9GfJTvNdoGjVqjXbaH3KkaWm/QRH0mh+17yynajt75 +HK1Us+sy+7N9zinHQ== =MRuJ -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
6ba0d2e4fc
|
@ -1520,18 +1520,34 @@ struct module_sect_attrs {
|
||||||
struct module_sect_attr 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,
|
static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
|
||||||
struct bin_attribute *battr,
|
struct bin_attribute *battr,
|
||||||
char *buf, loff_t pos, size_t count)
|
char *buf, loff_t pos, size_t count)
|
||||||
{
|
{
|
||||||
struct module_sect_attr *sattr =
|
struct module_sect_attr *sattr =
|
||||||
container_of(battr, struct module_sect_attr, battr);
|
container_of(battr, struct module_sect_attr, battr);
|
||||||
|
char bounce[MODULE_SECT_READ_SIZE + 1];
|
||||||
|
size_t wrote;
|
||||||
|
|
||||||
if (pos != 0)
|
if (pos != 0)
|
||||||
return -EINVAL;
|
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)
|
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;
|
goto out;
|
||||||
sect_attrs->nsections++;
|
sect_attrs->nsections++;
|
||||||
sattr->battr.read = module_sect_read;
|
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;
|
sattr->battr.attr.mode = 0400;
|
||||||
*(gattr++) = &(sattr++)->battr;
|
*(gattr++) = &(sattr++)->battr;
|
||||||
}
|
}
|
||||||
|
|
1
tools/testing/selftests/splice/.gitignore
vendored
1
tools/testing/selftests/splice/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
default_file_splice_read
|
default_file_splice_read
|
||||||
|
splice_read
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
TEST_PROGS := default_file_splice_read.sh
|
TEST_PROGS := default_file_splice_read.sh short_splice_read.sh
|
||||||
TEST_GEN_PROGS_EXTENDED := default_file_splice_read
|
TEST_GEN_PROGS_EXTENDED := default_file_splice_read splice_read
|
||||||
|
|
||||||
include ../lib.mk
|
include ../lib.mk
|
||||||
|
|
1
tools/testing/selftests/splice/config
Normal file
1
tools/testing/selftests/splice/config
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CONFIG_TEST_LKM=m
|
1
tools/testing/selftests/splice/settings
Normal file
1
tools/testing/selftests/splice/settings
Normal file
|
@ -0,0 +1 @@
|
||||||
|
timeout=5
|
56
tools/testing/selftests/splice/short_splice_read.sh
Executable file
56
tools/testing/selftests/splice/short_splice_read.sh
Executable file
|
@ -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
|
57
tools/testing/selftests/splice/splice_read.c
Normal file
57
tools/testing/selftests/splice/splice_read.c
Normal file
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in a new issue