diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2026-10-01 14:52:55 +0000 |
|---|---|---|
| committer | Kumar Kartikeya Dwivedi <memxor@gmail.com> | 2026-10-01 18:39:27 +0200 |
| commit | 33a154a96e71a34a1bcca9f40da343dbbf7b38b4 (patch) | |
| tree | 543be8029ab3d8a3a63994ae23385a053ed30029 /lib/test_sysctl.c | |
| download | linux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.tar.gz linux-stable-33a154a96e71a34a1bcca9f40da343dbbf7b38b4.zip | |
selftests/bpf: Test packet range of pointers sharing an idgrafted
Add tests where two packet pointers share an id and tightening one
pointer's umax from its var_off would put it less than their constant
distance from the other's umax: with an index & 0x38 capped at 50, the
base pointer keeps umax 50, so the pointer 8 bytes further on must keep
umax 58, even though its known bits allow at most 56.
These refused a valid program or accepted an out-of-bounds access before
the fix:
- check the advanced copy, load through the base: valid, was refused;
- check the base, load the byte at base + 1 through a copy advanced by
8: was accepted;
- check base + 4, load 4 bytes at base + 2 through base + 8: reads two
bytes past the checked range, was accepted;
- the same as the second with data_meta pointers checked against data:
was accepted.
These pass with and without the fix and cover nearby paths:
- subtract an unknown scalar from a checked pointer and load below it
(the range is kept across a new id);
- reach a load through two paths whose checks cover 8 and 7 bytes after
the loaded pointer; the second path must not be pruned by the first;
- spill a copy of a pointer, check the pointer, fill the copy and load
one byte past the checked range: the load is refused, and the copy
has the range of the check.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20261001145255.855630-2-alexei.starovoitov@gmail.com
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Diffstat (limited to 'lib/test_sysctl.c')
| -rw-r--r-- | lib/test_sysctl.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c new file mode 100644 index 000000000..909cfcf76 --- /dev/null +++ b/lib/test_sysctl.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR copyleft-next-0.3.1 +/* + * proc sysctl test driver + * + * Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org> + */ + +/* + * This module provides an interface to the proc sysctl interfaces. This + * driver requires CONFIG_SYSCTL. It will not normally be loaded by the + * system unless explicitly requested by name. You can also build this driver + * into your kernel. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/async.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> + +static int i_zero; +static int i_one_hundred = 100; +static int match_int_ok = 1; + +enum { + TEST_H_SETUP_NODE, + TEST_H_MNT, + TEST_H_MNTERROR, + TEST_H_EMPTY_ADD, + TEST_H_EMPTY, + TEST_H_U8, + TEST_H_SIZE /* Always at the end */ +}; + +static struct ctl_table_header *ctl_headers[TEST_H_SIZE] = {}; +struct test_sysctl_data { + int int_0001; + int int_0002; + int int_0003[4]; + + int boot_int; + + unsigned int uint_0001; + + char string_0001[65]; + +#define SYSCTL_TEST_BITMAP_SIZE 65536 + unsigned long *bitmap_0001; +}; + +static struct test_sysctl_data test_data = { + .int_0001 = 60, + .int_0002 = 1, + + .int_0003[0] = 0, + .int_0003[1] = 1, + .int_0003[2] = 2, + .int_0003[3] = 3, + + .boot_int = 0, + + .uint_0001 = 314, + + .string_0001 = "(none)", +}; + +/* These are all under /proc/sys/debug/test_sysctl/ */ +static const struct ctl_table test_table[] = { + { + .procname = "int_0001", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }, + { + .procname = "int_0002", + .data = &test_data.int_0002, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "int_0003", + .data = &test_data.int_0003, + .maxlen = sizeof(test_data.int_0003), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "match_int", + .data = &match_int_ok, + .maxlen = sizeof(match_int_ok), + .mode = 0444, + .proc_handler = proc_dointvec, + }, + { + .procname = "boot_int", + .data = &test_data.boot_int, + .maxlen = sizeof(test_data.boot_int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, + { + .procname = "uint_0001", + .data = &test_data.uint_0001, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_douintvec, + }, + { + .procname = "string_0001", + .data = &test_data.string_0001, + .maxlen = sizeof(test_data.string_0001), + .mode = 0644, + .proc_handler = proc_dostring, + }, + { + .procname = "bitmap_0001", + .data = &test_data.bitmap_0001, + .maxlen = SYSCTL_TEST_BITMAP_SIZE, + .mode = 0644, + .proc_handler = proc_do_large_bitmap, + }, +}; + +static void test_sysctl_calc_match_int_ok(void) +{ + int i; + + struct { + int defined; + int wanted; + } match_int[] = { + {.defined = *(int *)SYSCTL_ZERO, .wanted = 0}, + {.defined = *(int *)SYSCTL_ONE, .wanted = 1}, + {.defined = *(int *)SYSCTL_TWO, .wanted = 2}, + {.defined = *(int *)SYSCTL_THREE, .wanted = 3}, + {.defined = *(int *)SYSCTL_FOUR, .wanted = 4}, + {.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100}, + {.defined = *(int *)SYSCTL_TWO_HUNDRED, .wanted = 200}, + {.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000}, + {.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000}, + {.defined = *(int *)SYSCTL_INT_MAX, .wanted = INT_MAX}, + {.defined = *(int *)SYSCTL_MAXOLDUID, .wanted = 65535}, + {.defined = *(int *)SYSCTL_NEG_ONE, .wanted = -1}, + }; + + for (i = 0; i < ARRAY_SIZE(match_int); i++) + if (match_int[i].defined != match_int[i].wanted) + match_int_ok = 0; +} + +static int test_sysctl_setup_node_tests(void) +{ + test_sysctl_calc_match_int_ok(); + test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL); + if (!test_data.bitmap_0001) + return -ENOMEM; + ctl_headers[TEST_H_SETUP_NODE] = register_sysctl("debug/test_sysctl", test_table); + if (!ctl_headers[TEST_H_SETUP_NODE]) { + kfree(test_data.bitmap_0001); + return -ENOMEM; + } + + return 0; +} + +/* Used to test that unregister actually removes the directory */ +static const struct ctl_table test_table_unregister[] = { + { + .procname = "unregister_error", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + }, +}; + +static int test_sysctl_run_unregister_nested(void) +{ + struct ctl_table_header *unregister; + + unregister = register_sysctl("debug/test_sysctl/unregister_error", + test_table_unregister); + if (!unregister) + return -ENOMEM; + + unregister_sysctl_table(unregister); + return 0; +} + +static int test_sysctl_run_register_mount_point(void) +{ + ctl_headers[TEST_H_MNT] + = register_sysctl_mount_point("debug/test_sysctl/mnt"); + if (!ctl_headers[TEST_H_MNT]) + return -ENOMEM; + + ctl_headers[TEST_H_MNTERROR] + = register_sysctl("debug/test_sysctl/mnt/mnt_error", + test_table_unregister); + /* + * Don't check the result.: + * If it fails (expected behavior), return 0. + * If successful (missbehavior of register mount point), we want to see + * mnt_error when we run the sysctl test script + */ + + return 0; +} + +static const struct ctl_table test_table_empty[] = { }; + +static int test_sysctl_run_register_empty(void) +{ + /* Tets that an empty dir can be created */ + ctl_headers[TEST_H_EMPTY_ADD] + = register_sysctl("debug/test_sysctl/empty_add", test_table_empty); + if (!ctl_headers[TEST_H_EMPTY_ADD]) + return -ENOMEM; + + /* Test that register on top of an empty dir works */ + ctl_headers[TEST_H_EMPTY] + = register_sysctl("debug/test_sysctl/empty_add/empty", test_table_empty); + if (!ctl_headers[TEST_H_EMPTY]) + return -ENOMEM; + + return 0; +} + +static const struct ctl_table table_u8_over[] = { + { + .procname = "u8_over", + .data = &test_data.uint_0001, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_FOUR, + .extra2 = SYSCTL_ONE_THOUSAND, + }, +}; + +static const struct ctl_table table_u8_under[] = { + { + .procname = "u8_under", + .data = &test_data.uint_0001, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_NEG_ONE, + .extra2 = SYSCTL_ONE_HUNDRED, + }, +}; + +static const struct ctl_table table_u8_valid[] = { + { + .procname = "u8_valid", + .data = &test_data.uint_0001, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_TWO_HUNDRED, + }, +}; + +static int test_sysctl_register_u8_extra(void) +{ + /* should fail because it's over */ + ctl_headers[TEST_H_U8] + = register_sysctl("debug/test_sysctl", table_u8_over); + if (ctl_headers[TEST_H_U8]) + return -ENOMEM; + + /* should fail because it's under */ + ctl_headers[TEST_H_U8] + = register_sysctl("debug/test_sysctl", table_u8_under); + if (ctl_headers[TEST_H_U8]) + return -ENOMEM; + + /* should not fail because it's valid */ + ctl_headers[TEST_H_U8] + = register_sysctl("debug/test_sysctl", table_u8_valid); + if (!ctl_headers[TEST_H_U8]) + return -ENOMEM; + + return 0; +} + +static int __init test_sysctl_init(void) +{ + int err = 0; + + int (*func_array[])(void) = { + test_sysctl_setup_node_tests, + test_sysctl_run_unregister_nested, + test_sysctl_run_register_mount_point, + test_sysctl_run_register_empty, + test_sysctl_register_u8_extra + }; + + for (int i = 0; !err && i < ARRAY_SIZE(func_array); i++) + err = func_array[i](); + + return err; +} +module_init(test_sysctl_init); + +static void __exit test_sysctl_exit(void) +{ + kfree(test_data.bitmap_0001); + for (int i = 0; i < TEST_H_SIZE; i++) { + if (ctl_headers[i]) + unregister_sysctl_table(ctl_headers[i]); + } +} + +module_exit(test_sysctl_exit); + +MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>"); +MODULE_DESCRIPTION("proc sysctl test driver"); +MODULE_LICENSE("GPL"); |
