From: Thomas Weißschuh Date: Mon, 24 Apr 2023 16:35:27 +0000 (+0200) Subject: tests: add test_enosys helper X-Git-Tag: v2.39~55^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c93114cdbec6eaa860aeda11b1ce8c455273539a;p=thirdparty%2Futil-linux.git tests: add test_enosys helper This helper can be used to block certain syscalls with ENOSYS for executed programs. This allows testing of fallback codepaths inside util-linux. Signed-off-by: Thomas Weißschuh --- diff --git a/meson.build b/meson.build index f0ebe07414..931284152c 100644 --- a/meson.build +++ b/meson.build @@ -3229,6 +3229,12 @@ exe = executable( include_directories : includes) exes += exe +exe = executable( + 'test_enosys', + 'tests/helpers/test_enosys.c', + include_directories : includes) +exes += exe + ############################################################ # XXX: HAVE_OPENAT diff --git a/tests/commands.sh b/tests/commands.sh index 12aac2dd6d..8dfb135a8e 100644 --- a/tests/commands.sh +++ b/tests/commands.sh @@ -5,6 +5,7 @@ TS_TESTUSER=${TS_TESTUSER:-"nobody"} TS_HELPER_BYTESWAP="${ts_helpersdir}test_byteswap" TS_HELPER_CPUSET="${ts_helpersdir}test_cpuset" TS_HELPER_DMESG="${ts_helpersdir}test_dmesg" +TS_HELPER_ENOSYS="${ts_helpersdir}test_enosys" TS_HELPER_ISLOCAL="${ts_helpersdir}test_islocal" TS_HELPER_ISMOUNTED="${ts_helpersdir}test_ismounted" TS_HELPER_LIBFDISK_GPT="${ts_helpersdir}test_fdisk_gpt" diff --git a/tests/helpers/Makemodule.am b/tests/helpers/Makemodule.am index 83df24b1f0..2b1df3c6ba 100644 --- a/tests/helpers/Makemodule.am +++ b/tests/helpers/Makemodule.am @@ -34,4 +34,7 @@ test_uuid_namespace_SOURCES = tests/helpers/test_uuid_namespace.c \ if LINUX check_PROGRAMS += test_mkfds test_mkfds_SOURCES = tests/helpers/test_mkfds.c + +check_PROGRAMS += test_enosys +test_enosys_SOURCES = tests/helpers/test_enosys.c endif diff --git a/tests/helpers/test_enosys.c b/tests/helpers/test_enosys.c new file mode 100644 index 0000000000..f8e97bee58 --- /dev/null +++ b/tests/helpers/test_enosys.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2023 Thomas Weißschuh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "c.h" +#include "exitcodes.h" + +#if __x86_64__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_X86_64 +#elif __i386__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_I386 +#elif __arm__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ARM +#elif __aarch64__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_AARCH64 +#elif __riscv +# if __riscv_xlen == 32 +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_RISCV32 +# elif __riscv_xlen == 64 +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_RISCV64 +# endif +#elif __s390__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390 +#elif __s390x__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390X +#elif __PPC64__ +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PPC64 +# else +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PPC64LE +# endif +#else +# error Unknown target architecture +#endif + +#define syscall_nr (offsetof(struct seccomp_data, nr)) + +struct syscall { + const char *const name; + int number; +}; + +const struct syscall syscalls[] = { + { "move_mount", __NR_move_mount }, + { "open_tree", __NR_open_tree }, + { "fsopen", __NR_fsopen }, +}; + +int main(int argc, char **argv) +{ + char c; + size_t i; + bool found; + static const struct option longopts[] = { + { "syscall", required_argument, NULL, 's' }, + { 0 } + }; + + bool blocked_syscalls[ARRAY_SIZE(syscalls)] = {}; + + while ((c = getopt_long (argc, argv, "s:", longopts, NULL)) != -1) { + switch (c) { + case 's': + found = 0; + for (i = 0; i < ARRAY_SIZE(syscalls); i++) { + if (strcmp(optarg, syscalls[i].name) == 0) { + blocked_syscalls[i] = true; + found = 1; + break; + } + } + if (!found) + errx(EXIT_FAILURE, "Unknown syscall '%s'", optarg); + break; + default: + errx(EXIT_FAILURE, "Unknown option"); + } + } + + if (optind >= argc) + errx(EXIT_FAILURE, "No executable specified"); + +#define N_FILTERS (ARRAY_SIZE(syscalls) + 3) + + struct sock_filter filter[N_FILTERS] = { + [0] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr), + + [N_FILTERS - 2] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + [N_FILTERS - 1] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS), + }; + + const struct sock_filter nop = BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0); + + for (i = 0; i < ARRAY_SIZE(syscalls); i++) { + if (blocked_syscalls[i]) { + const struct sock_filter block = BPF_JUMP( + BPF_JMP | BPF_JEQ | BPF_K, + syscalls[i].number, + N_FILTERS - 3 - i, 0); + filter[i + 1] = block; + } else { + filter[i + 1] = nop; + } + } + + struct sock_fprog prog = { + .len = ARRAY_SIZE(filter), + .filter = filter, + }; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + return EXIT_NOTSUPP; + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) + return EXIT_NOTSUPP; + + if (execvp(argv[optind], argv + optind)) + err(EXIT_NOTSUPP, "Could not exec"); +}