From b6f5ee4d53019443fb99dd23bc08680b1244ccfa Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 20 Jul 2024 10:03:14 -0700 Subject: execve: Move KUnit tests to tests/ subdirectory Move the exec KUnit tests into a separate directory to avoid polluting the local directory namespace. Additionally update MAINTAINERS for the new files. Reviewed-by: David Gow Reviewed-by: SeongJae Park Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20240720170310.it.942-kees@kernel.org Signed-off-by: Kees Cook --- MAINTAINERS | 3 +- fs/binfmt_elf.c | 2 +- fs/binfmt_elf_test.c | 64 -------------------- fs/exec.c | 2 +- fs/exec_test.c | 141 -------------------------------------------- fs/tests/binfmt_elf_kunit.c | 64 ++++++++++++++++++++ fs/tests/exec_kunit.c | 141 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 208 deletions(-) delete mode 100644 fs/binfmt_elf_test.c delete mode 100644 fs/exec_test.c create mode 100644 fs/tests/binfmt_elf_kunit.c create mode 100644 fs/tests/exec_kunit.c diff --git a/MAINTAINERS b/MAINTAINERS index 8dfbe998f175..5fa7e129a59e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8220,7 +8220,8 @@ F: Documentation/userspace-api/ELF.rst F: fs/*binfmt_*.c F: fs/Kconfig.binfmt F: fs/exec.c -F: fs/exec_test.c +F: fs/tests/binfmt_*_kunit.c +F: fs/tests/exec_kunit.c F: include/linux/binfmts.h F: include/linux/elf.h F: include/uapi/linux/binfmts.h diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 40111451aa95..04e748c5955f 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -2152,5 +2152,5 @@ core_initcall(init_elf_binfmt); module_exit(exit_elf_binfmt); #ifdef CONFIG_BINFMT_ELF_KUNIT_TEST -#include "binfmt_elf_test.c" +#include "tests/binfmt_elf_kunit.c" #endif diff --git a/fs/binfmt_elf_test.c b/fs/binfmt_elf_test.c deleted file mode 100644 index 11d734fec366..000000000000 --- a/fs/binfmt_elf_test.c +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include - -static void total_mapping_size_test(struct kunit *test) -{ - struct elf_phdr empty[] = { - { .p_type = PT_LOAD, .p_vaddr = 0, .p_memsz = 0, }, - { .p_type = PT_INTERP, .p_vaddr = 10, .p_memsz = 999999, }, - }; - /* - * readelf -lW /bin/mount | grep '^ .*0x0' | awk '{print "\t\t{ .p_type = PT_" \ - * $1 ", .p_vaddr = " $3 ", .p_memsz = " $6 ", },"}' - */ - struct elf_phdr mount[] = { - { .p_type = PT_PHDR, .p_vaddr = 0x00000040, .p_memsz = 0x0002d8, }, - { .p_type = PT_INTERP, .p_vaddr = 0x00000318, .p_memsz = 0x00001c, }, - { .p_type = PT_LOAD, .p_vaddr = 0x00000000, .p_memsz = 0x0033a8, }, - { .p_type = PT_LOAD, .p_vaddr = 0x00004000, .p_memsz = 0x005c91, }, - { .p_type = PT_LOAD, .p_vaddr = 0x0000a000, .p_memsz = 0x0022f8, }, - { .p_type = PT_LOAD, .p_vaddr = 0x0000d330, .p_memsz = 0x000d40, }, - { .p_type = PT_DYNAMIC, .p_vaddr = 0x0000d928, .p_memsz = 0x000200, }, - { .p_type = PT_NOTE, .p_vaddr = 0x00000338, .p_memsz = 0x000030, }, - { .p_type = PT_NOTE, .p_vaddr = 0x00000368, .p_memsz = 0x000044, }, - { .p_type = PT_GNU_PROPERTY, .p_vaddr = 0x00000338, .p_memsz = 0x000030, }, - { .p_type = PT_GNU_EH_FRAME, .p_vaddr = 0x0000b490, .p_memsz = 0x0001ec, }, - { .p_type = PT_GNU_STACK, .p_vaddr = 0x00000000, .p_memsz = 0x000000, }, - { .p_type = PT_GNU_RELRO, .p_vaddr = 0x0000d330, .p_memsz = 0x000cd0, }, - }; - size_t mount_size = 0xE070; - /* https://lore.kernel.org/linux-fsdevel/YfF18Dy85mCntXrx@fractal.localdomain */ - struct elf_phdr unordered[] = { - { .p_type = PT_LOAD, .p_vaddr = 0x00000000, .p_memsz = 0x0033a8, }, - { .p_type = PT_LOAD, .p_vaddr = 0x0000d330, .p_memsz = 0x000d40, }, - { .p_type = PT_LOAD, .p_vaddr = 0x00004000, .p_memsz = 0x005c91, }, - { .p_type = PT_LOAD, .p_vaddr = 0x0000a000, .p_memsz = 0x0022f8, }, - }; - - /* No headers, no size. */ - KUNIT_EXPECT_EQ(test, total_mapping_size(NULL, 0), 0); - KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 0), 0); - /* Empty headers, no size. */ - KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 1), 0); - /* No PT_LOAD headers, no size. */ - KUNIT_EXPECT_EQ(test, total_mapping_size(&empty[1], 1), 0); - /* Empty PT_LOAD and non-PT_LOAD headers, no size. */ - KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 2), 0); - - /* Normal set of PT_LOADS, and expected size. */ - KUNIT_EXPECT_EQ(test, total_mapping_size(mount, ARRAY_SIZE(mount)), mount_size); - /* Unordered PT_LOADs result in same size. */ - KUNIT_EXPECT_EQ(test, total_mapping_size(unordered, ARRAY_SIZE(unordered)), mount_size); -} - -static struct kunit_case binfmt_elf_test_cases[] = { - KUNIT_CASE(total_mapping_size_test), - {}, -}; - -static struct kunit_suite binfmt_elf_test_suite = { - .name = KBUILD_MODNAME, - .test_cases = binfmt_elf_test_cases, -}; - -kunit_test_suite(binfmt_elf_test_suite); diff --git a/fs/exec.c b/fs/exec.c index 5b580ff8d955..32d6537ece07 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2244,5 +2244,5 @@ fs_initcall(init_fs_exec_sysctls); #endif /* CONFIG_SYSCTL */ #ifdef CONFIG_EXEC_KUNIT_TEST -#include "exec_test.c" +#include "tests/exec_kunit.c" #endif diff --git a/fs/exec_test.c b/fs/exec_test.c deleted file mode 100644 index 7c77d039680b..000000000000 --- a/fs/exec_test.c +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include - -struct bprm_stack_limits_result { - struct linux_binprm bprm; - int expected_rc; - unsigned long expected_argmin; -}; - -static const struct bprm_stack_limits_result bprm_stack_limits_results[] = { - /* Negative argc/envc counts produce -E2BIG */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = INT_MIN, .envc = INT_MIN }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = 5, .envc = -1 }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = -1, .envc = 10 }, .expected_rc = -E2BIG }, - /* The max value of argc or envc is MAX_ARG_STRINGS. */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = INT_MAX, .envc = INT_MAX }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = MAX_ARG_STRINGS, .envc = MAX_ARG_STRINGS }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = 0, .envc = MAX_ARG_STRINGS }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = MAX_ARG_STRINGS, .envc = 0 }, .expected_rc = -E2BIG }, - /* - * On 32-bit system these argc and envc counts, while likely impossible - * to represent within the associated TASK_SIZE, could overflow the - * limit calculation, and bypass the ptr_size <= limit check. - */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, - .argc = 0x20000001, .envc = 0x20000001 }, .expected_rc = -E2BIG }, -#ifdef CONFIG_MMU - /* Make sure a pathological bprm->p doesn't cause an overflow. */ - { { .p = sizeof(void *), .rlim_stack.rlim_cur = ULONG_MAX, - .argc = 10, .envc = 10 }, .expected_rc = -E2BIG }, -#endif - /* - * 0 rlim_stack will get raised to ARG_MAX. With 1 string pointer, - * we should see p - ARG_MAX + sizeof(void *). - */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = 1, .envc = 0 }, .expected_argmin = ULONG_MAX - ARG_MAX + sizeof(void *)}, - /* Validate that argc is always raised to a minimum of 1. */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = 0, .envc = 0 }, .expected_argmin = ULONG_MAX - ARG_MAX + sizeof(void *)}, - /* - * 0 rlim_stack will get raised to ARG_MAX. With pointers filling ARG_MAX, - * we should see -E2BIG. (Note argc is always raised to at least 1.) - */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = ARG_MAX / sizeof(void *), .envc = 0 }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = 0, .envc = ARG_MAX / sizeof(void *) - 1 }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = ARG_MAX / sizeof(void *) + 1, .envc = 0 }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = 0, .envc = ARG_MAX / sizeof(void *) }, .expected_rc = -E2BIG }, - /* And with one less, we see space for exactly 1 pointer. */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = (ARG_MAX / sizeof(void *)) - 1, .envc = 0 }, - .expected_argmin = ULONG_MAX - sizeof(void *) }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, - .argc = 0, .envc = (ARG_MAX / sizeof(void *)) - 2, }, - .expected_argmin = ULONG_MAX - sizeof(void *) }, - /* If we raise rlim_stack / 4 to exactly ARG_MAX, nothing changes. */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, - .argc = ARG_MAX / sizeof(void *), .envc = 0 }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, - .argc = 0, .envc = ARG_MAX / sizeof(void *) - 1 }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, - .argc = ARG_MAX / sizeof(void *) + 1, .envc = 0 }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, - .argc = 0, .envc = ARG_MAX / sizeof(void *) }, .expected_rc = -E2BIG }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, - .argc = (ARG_MAX / sizeof(void *)) - 1, .envc = 0 }, - .expected_argmin = ULONG_MAX - sizeof(void *) }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, - .argc = 0, .envc = (ARG_MAX / sizeof(void *)) - 2, }, - .expected_argmin = ULONG_MAX - sizeof(void *) }, - /* But raising it another pointer * 4 will provide space for 1 more pointer. */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = (ARG_MAX + sizeof(void *)) * 4, - .argc = ARG_MAX / sizeof(void *), .envc = 0 }, - .expected_argmin = ULONG_MAX - sizeof(void *) }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = (ARG_MAX + sizeof(void *)) * 4, - .argc = 0, .envc = ARG_MAX / sizeof(void *) - 1 }, - .expected_argmin = ULONG_MAX - sizeof(void *) }, - /* Raising rlim_stack / 4 to _STK_LIM / 4 * 3 will see more space. */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3), - .argc = 0, .envc = 0 }, - .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3), - .argc = 0, .envc = 0 }, - .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, - /* But raising it any further will see no increase. */ - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3 + sizeof(void *)), - .argc = 0, .envc = 0 }, - .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * + sizeof(void *)), - .argc = 0, .envc = 0 }, - .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * _STK_LIM, - .argc = 0, .envc = 0 }, - .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, - { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * _STK_LIM, - .argc = 0, .envc = 0 }, - .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, -}; - -static void exec_test_bprm_stack_limits(struct kunit *test) -{ - /* Double-check the constants. */ - KUNIT_EXPECT_EQ(test, _STK_LIM, SZ_8M); - KUNIT_EXPECT_EQ(test, ARG_MAX, 32 * SZ_4K); - KUNIT_EXPECT_EQ(test, MAX_ARG_STRINGS, 0x7FFFFFFF); - - for (int i = 0; i < ARRAY_SIZE(bprm_stack_limits_results); i++) { - const struct bprm_stack_limits_result *result = &bprm_stack_limits_results[i]; - struct linux_binprm bprm = result->bprm; - int rc; - - rc = bprm_stack_limits(&bprm); - KUNIT_EXPECT_EQ_MSG(test, rc, result->expected_rc, "on loop %d", i); -#ifdef CONFIG_MMU - KUNIT_EXPECT_EQ_MSG(test, bprm.argmin, result->expected_argmin, "on loop %d", i); -#endif - } -} - -static struct kunit_case exec_test_cases[] = { - KUNIT_CASE(exec_test_bprm_stack_limits), - {}, -}; - -static struct kunit_suite exec_test_suite = { - .name = "exec", - .test_cases = exec_test_cases, -}; - -kunit_test_suite(exec_test_suite); diff --git a/fs/tests/binfmt_elf_kunit.c b/fs/tests/binfmt_elf_kunit.c new file mode 100644 index 000000000000..11d734fec366 --- /dev/null +++ b/fs/tests/binfmt_elf_kunit.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include + +static void total_mapping_size_test(struct kunit *test) +{ + struct elf_phdr empty[] = { + { .p_type = PT_LOAD, .p_vaddr = 0, .p_memsz = 0, }, + { .p_type = PT_INTERP, .p_vaddr = 10, .p_memsz = 999999, }, + }; + /* + * readelf -lW /bin/mount | grep '^ .*0x0' | awk '{print "\t\t{ .p_type = PT_" \ + * $1 ", .p_vaddr = " $3 ", .p_memsz = " $6 ", },"}' + */ + struct elf_phdr mount[] = { + { .p_type = PT_PHDR, .p_vaddr = 0x00000040, .p_memsz = 0x0002d8, }, + { .p_type = PT_INTERP, .p_vaddr = 0x00000318, .p_memsz = 0x00001c, }, + { .p_type = PT_LOAD, .p_vaddr = 0x00000000, .p_memsz = 0x0033a8, }, + { .p_type = PT_LOAD, .p_vaddr = 0x00004000, .p_memsz = 0x005c91, }, + { .p_type = PT_LOAD, .p_vaddr = 0x0000a000, .p_memsz = 0x0022f8, }, + { .p_type = PT_LOAD, .p_vaddr = 0x0000d330, .p_memsz = 0x000d40, }, + { .p_type = PT_DYNAMIC, .p_vaddr = 0x0000d928, .p_memsz = 0x000200, }, + { .p_type = PT_NOTE, .p_vaddr = 0x00000338, .p_memsz = 0x000030, }, + { .p_type = PT_NOTE, .p_vaddr = 0x00000368, .p_memsz = 0x000044, }, + { .p_type = PT_GNU_PROPERTY, .p_vaddr = 0x00000338, .p_memsz = 0x000030, }, + { .p_type = PT_GNU_EH_FRAME, .p_vaddr = 0x0000b490, .p_memsz = 0x0001ec, }, + { .p_type = PT_GNU_STACK, .p_vaddr = 0x00000000, .p_memsz = 0x000000, }, + { .p_type = PT_GNU_RELRO, .p_vaddr = 0x0000d330, .p_memsz = 0x000cd0, }, + }; + size_t mount_size = 0xE070; + /* https://lore.kernel.org/linux-fsdevel/YfF18Dy85mCntXrx@fractal.localdomain */ + struct elf_phdr unordered[] = { + { .p_type = PT_LOAD, .p_vaddr = 0x00000000, .p_memsz = 0x0033a8, }, + { .p_type = PT_LOAD, .p_vaddr = 0x0000d330, .p_memsz = 0x000d40, }, + { .p_type = PT_LOAD, .p_vaddr = 0x00004000, .p_memsz = 0x005c91, }, + { .p_type = PT_LOAD, .p_vaddr = 0x0000a000, .p_memsz = 0x0022f8, }, + }; + + /* No headers, no size. */ + KUNIT_EXPECT_EQ(test, total_mapping_size(NULL, 0), 0); + KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 0), 0); + /* Empty headers, no size. */ + KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 1), 0); + /* No PT_LOAD headers, no size. */ + KUNIT_EXPECT_EQ(test, total_mapping_size(&empty[1], 1), 0); + /* Empty PT_LOAD and non-PT_LOAD headers, no size. */ + KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 2), 0); + + /* Normal set of PT_LOADS, and expected size. */ + KUNIT_EXPECT_EQ(test, total_mapping_size(mount, ARRAY_SIZE(mount)), mount_size); + /* Unordered PT_LOADs result in same size. */ + KUNIT_EXPECT_EQ(test, total_mapping_size(unordered, ARRAY_SIZE(unordered)), mount_size); +} + +static struct kunit_case binfmt_elf_test_cases[] = { + KUNIT_CASE(total_mapping_size_test), + {}, +}; + +static struct kunit_suite binfmt_elf_test_suite = { + .name = KBUILD_MODNAME, + .test_cases = binfmt_elf_test_cases, +}; + +kunit_test_suite(binfmt_elf_test_suite); diff --git a/fs/tests/exec_kunit.c b/fs/tests/exec_kunit.c new file mode 100644 index 000000000000..7c77d039680b --- /dev/null +++ b/fs/tests/exec_kunit.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include + +struct bprm_stack_limits_result { + struct linux_binprm bprm; + int expected_rc; + unsigned long expected_argmin; +}; + +static const struct bprm_stack_limits_result bprm_stack_limits_results[] = { + /* Negative argc/envc counts produce -E2BIG */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = INT_MIN, .envc = INT_MIN }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = 5, .envc = -1 }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = -1, .envc = 10 }, .expected_rc = -E2BIG }, + /* The max value of argc or envc is MAX_ARG_STRINGS. */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = INT_MAX, .envc = INT_MAX }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = MAX_ARG_STRINGS, .envc = MAX_ARG_STRINGS }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = 0, .envc = MAX_ARG_STRINGS }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = MAX_ARG_STRINGS, .envc = 0 }, .expected_rc = -E2BIG }, + /* + * On 32-bit system these argc and envc counts, while likely impossible + * to represent within the associated TASK_SIZE, could overflow the + * limit calculation, and bypass the ptr_size <= limit check. + */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX, + .argc = 0x20000001, .envc = 0x20000001 }, .expected_rc = -E2BIG }, +#ifdef CONFIG_MMU + /* Make sure a pathological bprm->p doesn't cause an overflow. */ + { { .p = sizeof(void *), .rlim_stack.rlim_cur = ULONG_MAX, + .argc = 10, .envc = 10 }, .expected_rc = -E2BIG }, +#endif + /* + * 0 rlim_stack will get raised to ARG_MAX. With 1 string pointer, + * we should see p - ARG_MAX + sizeof(void *). + */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = 1, .envc = 0 }, .expected_argmin = ULONG_MAX - ARG_MAX + sizeof(void *)}, + /* Validate that argc is always raised to a minimum of 1. */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = 0, .envc = 0 }, .expected_argmin = ULONG_MAX - ARG_MAX + sizeof(void *)}, + /* + * 0 rlim_stack will get raised to ARG_MAX. With pointers filling ARG_MAX, + * we should see -E2BIG. (Note argc is always raised to at least 1.) + */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = ARG_MAX / sizeof(void *), .envc = 0 }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = 0, .envc = ARG_MAX / sizeof(void *) - 1 }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = ARG_MAX / sizeof(void *) + 1, .envc = 0 }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = 0, .envc = ARG_MAX / sizeof(void *) }, .expected_rc = -E2BIG }, + /* And with one less, we see space for exactly 1 pointer. */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = (ARG_MAX / sizeof(void *)) - 1, .envc = 0 }, + .expected_argmin = ULONG_MAX - sizeof(void *) }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 0, + .argc = 0, .envc = (ARG_MAX / sizeof(void *)) - 2, }, + .expected_argmin = ULONG_MAX - sizeof(void *) }, + /* If we raise rlim_stack / 4 to exactly ARG_MAX, nothing changes. */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, + .argc = ARG_MAX / sizeof(void *), .envc = 0 }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, + .argc = 0, .envc = ARG_MAX / sizeof(void *) - 1 }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, + .argc = ARG_MAX / sizeof(void *) + 1, .envc = 0 }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, + .argc = 0, .envc = ARG_MAX / sizeof(void *) }, .expected_rc = -E2BIG }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, + .argc = (ARG_MAX / sizeof(void *)) - 1, .envc = 0 }, + .expected_argmin = ULONG_MAX - sizeof(void *) }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = ARG_MAX * 4, + .argc = 0, .envc = (ARG_MAX / sizeof(void *)) - 2, }, + .expected_argmin = ULONG_MAX - sizeof(void *) }, + /* But raising it another pointer * 4 will provide space for 1 more pointer. */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = (ARG_MAX + sizeof(void *)) * 4, + .argc = ARG_MAX / sizeof(void *), .envc = 0 }, + .expected_argmin = ULONG_MAX - sizeof(void *) }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = (ARG_MAX + sizeof(void *)) * 4, + .argc = 0, .envc = ARG_MAX / sizeof(void *) - 1 }, + .expected_argmin = ULONG_MAX - sizeof(void *) }, + /* Raising rlim_stack / 4 to _STK_LIM / 4 * 3 will see more space. */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3), + .argc = 0, .envc = 0 }, + .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3), + .argc = 0, .envc = 0 }, + .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, + /* But raising it any further will see no increase. */ + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * 3 + sizeof(void *)), + .argc = 0, .envc = 0 }, + .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * (_STK_LIM / 4 * + sizeof(void *)), + .argc = 0, .envc = 0 }, + .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * _STK_LIM, + .argc = 0, .envc = 0 }, + .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, + { { .p = ULONG_MAX, .rlim_stack.rlim_cur = 4 * _STK_LIM, + .argc = 0, .envc = 0 }, + .expected_argmin = ULONG_MAX - (_STK_LIM / 4 * 3) + sizeof(void *) }, +}; + +static void exec_test_bprm_stack_limits(struct kunit *test) +{ + /* Double-check the constants. */ + KUNIT_EXPECT_EQ(test, _STK_LIM, SZ_8M); + KUNIT_EXPECT_EQ(test, ARG_MAX, 32 * SZ_4K); + KUNIT_EXPECT_EQ(test, MAX_ARG_STRINGS, 0x7FFFFFFF); + + for (int i = 0; i < ARRAY_SIZE(bprm_stack_limits_results); i++) { + const struct bprm_stack_limits_result *result = &bprm_stack_limits_results[i]; + struct linux_binprm bprm = result->bprm; + int rc; + + rc = bprm_stack_limits(&bprm); + KUNIT_EXPECT_EQ_MSG(test, rc, result->expected_rc, "on loop %d", i); +#ifdef CONFIG_MMU + KUNIT_EXPECT_EQ_MSG(test, bprm.argmin, result->expected_argmin, "on loop %d", i); +#endif + } +} + +static struct kunit_case exec_test_cases[] = { + KUNIT_CASE(exec_test_bprm_stack_limits), + {}, +}; + +static struct kunit_suite exec_test_suite = { + .name = "exec", + .test_cases = exec_test_cases, +}; + +kunit_test_suite(exec_test_suite); -- cgit v1.2.3