Skip to content

Commit

Permalink
Merge tag 'kallsyms_show_value-fix-v5.9-rc1' of git://git.kernel.org/…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
Linus Torvalds committed Aug 7, 2020
2 parents 1fa2c0a + 9af4766 commit 6ba0d2e
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 5 deletions.
22 changes: 19 additions & 3 deletions kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/splice/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
default_file_splice_read
splice_read
4 changes: 2 additions & 2 deletions tools/testing/selftests/splice/Makefile
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tools/testing/selftests/splice/config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_TEST_LKM=m
1 change: 1 addition & 0 deletions tools/testing/selftests/splice/settings
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
timeout=5
56 changes: 56 additions & 0 deletions tools/testing/selftests/splice/short_splice_read.sh
Original file line number Diff line number Diff line change
@@ -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 changes: 57 additions & 0 deletions tools/testing/selftests/splice/splice_read.c
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 6ba0d2e

Please sign in to comment.