Skip to content

Commit 6ba0d2e

Browse files
committed
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 <[email protected]> * 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
2 parents 1fa2c0a + 9af4766 commit 6ba0d2e

File tree

7 files changed

+137
-5
lines changed

7 files changed

+137
-5
lines changed

kernel/module.c

+19-3
Original file line numberDiff line numberDiff line change
@@ -1520,18 +1520,34 @@ struct module_sect_attrs {
15201520
struct module_sect_attr attrs[];
15211521
};
15221522

1523+
#define MODULE_SECT_READ_SIZE (3 /* "0x", "\n" */ + (BITS_PER_LONG / 4))
15231524
static ssize_t module_sect_read(struct file *file, struct kobject *kobj,
15241525
struct bin_attribute *battr,
15251526
char *buf, loff_t pos, size_t count)
15261527
{
15271528
struct module_sect_attr *sattr =
15281529
container_of(battr, struct module_sect_attr, battr);
1530+
char bounce[MODULE_SECT_READ_SIZE + 1];
1531+
size_t wrote;
15291532

15301533
if (pos != 0)
15311534
return -EINVAL;
15321535

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;
15351551
}
15361552

15371553
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)
15801596
goto out;
15811597
sect_attrs->nsections++;
15821598
sattr->battr.read = module_sect_read;
1583-
sattr->battr.size = 3 /* "0x", "\n" */ + (BITS_PER_LONG / 4);
1599+
sattr->battr.size = MODULE_SECT_READ_SIZE;
15841600
sattr->battr.attr.mode = 0400;
15851601
*(gattr++) = &(sattr++)->battr;
15861602
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
default_file_splice_read
3+
splice_read
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# 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
44

55
include ../lib.mk

tools/testing/selftests/splice/config

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CONFIG_TEST_LKM=m
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
timeout=5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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+
}

0 commit comments

Comments
 (0)