diff options
author | Karel Zak <kzak@redhat.com> | 2023-05-17 13:34:34 +0200 |
---|---|---|
committer | Karel Zak <kzak@redhat.com> | 2023-05-17 13:34:34 +0200 |
commit | bd2ebbd4eadd29efac994037c0e85e8ddad13248 (patch) | |
tree | c093e2c8e59a28d2eaf0cc5e921ea07e7a8970a0 | |
parent | 372e026e09382dae5385718567ecd97ad0637ece (diff) | |
parent | afb669afededdc2865bc132b41a4d70b8c2ee840 (diff) | |
download | util-linux-bd2ebbd4eadd29efac994037c0e85e8ddad13248.tar.gz |
Merge branch 'enosys' of https://github.com/t-8ch/util-linux
* 'enosys' of https://github.com/t-8ch/util-linux:
enosys: properly block execve syscall
enosys: don't require end-of-options marker
enosys: find syscalls at build time
enosys: remove long jumps from BPF
enosys: add --list
enosys: validate syscall architecture
enosys: add manpage
enosys: add bash completion
enosys: make messages useful for users
enosys: translate messages
enosys: add common arguments
enosys: add test
enosys: fix native arch for s390x
enosys: syscall numbers are "long"
enosys: mark variable static
enosys: remove unneeded inline variable declaration
enosys: improve checks for EXIT_NOTSUPP
enosys: move from tests/helpers/test_enosys.c
c.h: make err_nonsys available
-rwxr-xr-x | .github/workflows/cibuild-setup-ubuntu.sh | 1 | ||||
-rw-r--r-- | bash-completion/Makemodule.am | 3 | ||||
-rw-r--r-- | bash-completion/enosys | 37 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | include/c.h | 3 | ||||
-rw-r--r-- | meson.build | 19 | ||||
-rw-r--r-- | misc-utils/Makemodule.am | 18 | ||||
-rw-r--r-- | misc-utils/enosys.1.adoc | 60 | ||||
-rw-r--r-- | misc-utils/enosys.c | 204 | ||||
-rw-r--r-- | misc-utils/waitpid.c | 3 | ||||
-rw-r--r-- | tests/commands.sh | 1 | ||||
-rw-r--r-- | tests/expected/misc/enosys-basic | 4 | ||||
-rw-r--r-- | tests/expected/misc/enosys-exec | 1 | ||||
-rw-r--r-- | tests/helpers/test_enosys.c | 139 | ||||
-rwxr-xr-x | tests/ts/misc/enosys | 49 | ||||
-rwxr-xr-x | tests/ts/mount/fallback | 12 | ||||
-rw-r--r-- | tools/Makemodule.am | 4 | ||||
-rwxr-xr-x | tools/all_syscalls | 15 |
18 files changed, 451 insertions, 126 deletions
diff --git a/.github/workflows/cibuild-setup-ubuntu.sh b/.github/workflows/cibuild-setup-ubuntu.sh index 3ffa9fcb3..de9f8a85c 100755 --- a/.github/workflows/cibuild-setup-ubuntu.sh +++ b/.github/workflows/cibuild-setup-ubuntu.sh @@ -27,6 +27,7 @@ PACKAGES=( iproute2 dmsetup python3-dev + gawk ) PACKAGES_OPTIONAL=( diff --git a/bash-completion/Makemodule.am b/bash-completion/Makemodule.am index eb10f6f70..ac8926f2d 100644 --- a/bash-completion/Makemodule.am +++ b/bash-completion/Makemodule.am @@ -344,5 +344,8 @@ endif if BUILD_WAITPID dist_bashcompletion_DATA += bash-completion/waitpid endif +if BUILD_ENOSYS +dist_bashcompletion_DATA += bash-completion/enosys +endif endif # BUILD_BASH_COMPLETION diff --git a/bash-completion/enosys b/bash-completion/enosys new file mode 100644 index 000000000..61aff46c8 --- /dev/null +++ b/bash-completion/enosys @@ -0,0 +1,37 @@ +_waitpid_module() +{ + local cur prev OPTS + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-s'|'--syscall') + return 0 + ;; + '-l'|'--list') + return 0 + ;; + '-h'|'--help'|'-V'|'--version') + return 0 + ;; + esac + case $cur in + -*) + OPTS="--syscall + --list + --help + --version" + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) + return 0 + ;; + *) + _pids + return 0 + ;; + esac + local IFS=$'\n' + compopt -o filenames + COMPREPLY=( $(compgen -u -- $cur) ) + return 0 +} +complete -F _enosys_module enosys diff --git a/configure.ac b/configure.ac index 6fac9cb4f..871f9c198 100644 --- a/configure.ac +++ b/configure.ac @@ -1851,6 +1851,10 @@ UL_BUILD_INIT([waitpid], [check]) UL_REQUIRES_LINUX([waitpid]) AM_CONDITIONAL([BUILD_WAITPID], [test "x$build_waitpid" = xyes]) +UL_BUILD_INIT([enosys], [check]) +UL_REQUIRES_LINUX([enosys]) +AM_CONDITIONAL([BUILD_ENOSYS], [test "x$build_enosys" = xyes]) + UL_BUILD_INIT([getopt], [yes]) AM_CONDITIONAL([BUILD_GETOPT], [test "x$build_getopt" = xyes]) diff --git a/include/c.h b/include/c.h index 2b70e8d18..f9f90f3bb 100644 --- a/include/c.h +++ b/include/c.h @@ -294,6 +294,9 @@ void __err_oom(const char *file, unsigned int line) } #define err_oom() __err_oom(__FILE__, __LINE__) +#define err_nosys(exitcode, ...) \ + err(errno == ENOSYS ? EXIT_NOTSUPP : exitcode, __VA_ARGS__) + /* Don't use inline function to avoid '#include "nls.h"' in c.h */ diff --git a/meson.build b/meson.build index c56f83b1f..e1099cf54 100644 --- a/meson.build +++ b/meson.build @@ -2856,6 +2856,25 @@ if not is_disabler(exe) bashcompletions += ['waitpid'] endif +syscalls_h = custom_target('syscalls.h', + input : 'tools/all_syscalls', + output : 'syscalls.h', + command : ['bash', '@INPUT@', cc.cmd_array()], +) + +exe = executable( + 'enosys', + 'misc-utils/enosys.c', syscalls_h, + include_directories : includes, + link_with : [lib_common], + install_dir : usrbin_exec_dir, + install : true) +if not is_disabler(exe) + exes += exe + manadocs += ['misc-utils/enosys.1.adoc'] + bashcompletions += ['enosys'] +endif + ############################################################ opt = not get_option('build-schedutils').disabled() diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am index 71548c9f9..76fa9014a 100644 --- a/misc-utils/Makemodule.am +++ b/misc-utils/Makemodule.am @@ -297,3 +297,21 @@ waitpid_SOURCES = misc-utils/waitpid.c waitpid_LDADD = $(LDADD) libcommon.la waitpid_CFLAGS = $(AM_CFLAGS) endif + +if BUILD_ENOSYS + +misc-utils/enosys.c: syscalls.h + +syscalls.h: $(top_srcdir)/tools/all_syscalls + $(top_srcdir)/tools/all_syscalls $(CC) $(CFLAGS) + +-include syscalls.h.deps +CLEANFILES += syscalls.h syscalls.h.deps + +usrbin_exec_PROGRAMS += enosys +MANPAGES += misc-utils/enosys.1 +dist_noinst_DATA += misc-utils/enosys.1.adoc +enosys_SOURCES = misc-utils/enosys.c +enosys_LDADD = $(LDADD) libcommon.la +enosys_CFLAGS = $(AM_CFLAGS) +endif diff --git a/misc-utils/enosys.1.adoc b/misc-utils/enosys.1.adoc new file mode 100644 index 000000000..9ce272860 --- /dev/null +++ b/misc-utils/enosys.1.adoc @@ -0,0 +1,60 @@ +//po4a: entry man manual += enosys(1) +:doctype: manpage +:man manual: User Commands +:man source: util-linux {release-version} +:page-layout: base +:command: enosys + +== NAME + +enosys - utility make syscalls fail with ENOSYS + +== SYNOPSIS + +*enosys* [*--syscall*|*-s* _syscall_] command + +== DESCRIPTION + +*enosys* is a simple command to execute a child process for which certain +syscalls fail with errno ENOSYS. + +It can be used to test the behavior of applications in the face of missing +syscalls as would happen when running on old kernels. + +== OPTIONS + +*-s*, *--syscall*:: +Syscall to block. Can be specified multiple times. + +*-l*, *--list*:: +List syscalls known to *enosys*. + +include::man-common/help-version.adoc[] + +== EXIT STATUS + +*enosys* exits with the status code of the executed process. +The following values have special meanings: + +*1*:: +internal error + +*2*:: +system does not provide the necessary functionality + +== AUTHORS + +mailto:thomas@t-8ch.de[Thomas Weißschuh] + +== SEE ALSO + +*syscall*(2) + +include::man-common/bugreports.adoc[] + +include::man-common/footer.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/misc-utils/enosys.c b/misc-utils/enosys.c new file mode 100644 index 000000000..1aa673717 --- /dev/null +++ b/misc-utils/enosys.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de> + * + * 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 <stddef.h> +#include <stdbool.h> +#include <getopt.h> + +#include <linux/unistd.h> +#include <linux/filter.h> +#include <linux/seccomp.h> +#include <linux/audit.h> +#include <sys/prctl.h> + +#include "c.h" +#include "exitcodes.h" +#include "nls.h" +#include "bitops.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 __s390x__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390X +#elif __s390__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390 +#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 UL_BPF_NOP (struct sock_filter) BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0) +#define IS_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + +#define syscall_nr (offsetof(struct seccomp_data, nr)) +#define syscall_arch (offsetof(struct seccomp_data, arch)) +#define syscall_arg(n) (offsetof(struct seccomp_data, args[n])) + +struct syscall { + const char *const name; + long number; +}; + +static const struct syscall syscalls[] = { +#define UL_SYSCALL(name, nr) { name, nr }, +#include "syscalls.h" +#undef UL_SYSCALL +}; +static_assert(sizeof(syscalls) > 0, "no syscalls found"); + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + + fputs(USAGE_HEADER, out); + fprintf(out, _(" %s [options] -- <command>\n"), program_invocation_short_name); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -s, --syscall syscall to block\n"), out); + fputs(_(" -l, --list list known syscalls\n"), out); + + fputs(USAGE_SEPARATOR, out); + fprintf(out, USAGE_HELP_OPTIONS(25)); + + fprintf(out, USAGE_MAN_TAIL("enosys(1)")); + + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + int c; + size_t i; + bool found; + static const struct option longopts[] = { + { "syscall", required_argument, NULL, 's' }, + { "list", no_argument, NULL, 'l' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { 0 } + }; + + bool blocked_syscalls[ARRAY_SIZE(syscalls)] = {}; + + while ((c = getopt_long (argc, argv, "+Vhs:l", 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; + case 'l': + for (i = 0; i < ARRAY_SIZE(syscalls); i++) + printf("%s\n", syscalls[i].name); + return EXIT_SUCCESS; + case 'V': + print_version(EXIT_SUCCESS); + case 'h': + usage(); + default: + errtryhelp(EXIT_FAILURE); + } + } + + if (optind >= argc) + errtryhelp(EXIT_FAILURE); + +#define N_FILTERS (ARRAY_SIZE(syscalls) * 2 + 12) + + struct sock_filter filter[N_FILTERS] = { + [0] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arch), + [1] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_ARCH_NATIVE, 1, 0), + [2] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP), + + /* Blocking "execve" normally would also block our own call to + * it and the end of main. To distinguish between our execve + * and the execve to be blocked, compare the environ pointer. + * + * See https://lore.kernel.org/all/CAAnLoWnS74dK9Wq4EQ-uzQ0qCRfSK-dLqh+HCais-5qwDjrVzg@mail.gmail.com/ + */ + [3] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr), + [4] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_execve, 0, 5), + [5] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(2) + 4 * !IS_LITTLE_ENDIAN), + [6] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t)(uintptr_t) environ, 0, 3), + [7] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_arg(2) + 4 * IS_LITTLE_ENDIAN), + [8] = BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (uint64_t)(uintptr_t) environ >> 32, 0, 1), + [9] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + [10] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr), + + [N_FILTERS - 1] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + }; + static_assert(ARRAY_SIZE(filter) <= BPF_MAXINSNS, "bpf filter too big"); + + for (i = 0; i < ARRAY_SIZE(syscalls); i++) { + struct sock_filter *f = &filter[11 + i * 2]; + + *f = (struct sock_filter) BPF_JUMP( + BPF_JMP | BPF_JEQ | BPF_K, + syscalls[i].number, + 0, 1); + *(f + 1) = blocked_syscalls[i] + ? (struct sock_filter) BPF_STMT( + BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS) + : UL_BPF_NOP; + } + + struct sock_fprog prog = { + .len = ARRAY_SIZE(filter), + .filter = filter, + }; + + /* *SET* below will return EINVAL when either the filter is invalid or + * seccomp is not supported. To distinguish those cases do a *GET* here + */ + if (prctl(PR_GET_SECCOMP) == -1 && errno == EINVAL) + err(EXIT_NOTSUPP, _("Seccomp non-functional")); + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + err_nosys(EXIT_FAILURE, _("Could not run prctl(PR_SET_NO_NEW_PRIVS)")); + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) + err_nosys(EXIT_FAILURE, _("Could not run prctl(PR_SET_SECCOMP)")); + + if (execvp(argv[optind], argv + optind)) + err(EXIT_NOTSUPP, _("Could not exec")); +} diff --git a/misc-utils/waitpid.c b/misc-utils/waitpid.c index b01a2f013..faf86ab6b 100644 --- a/misc-utils/waitpid.c +++ b/misc-utils/waitpid.c @@ -41,9 +41,6 @@ #define TIMEOUT_SOCKET_IDX UINT64_MAX -#define err_nosys(exitcode, ...) \ - err(errno == ENOSYS ? EXIT_NOTSUPP : exitcode, __VA_ARGS__) - static bool verbose = false; static struct timespec timeout; static bool allow_exited = false; diff --git a/tests/commands.sh b/tests/commands.sh index 8dfb135a8..c9a5cd129 100644 --- a/tests/commands.sh +++ b/tests/commands.sh @@ -60,6 +60,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"} TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"} TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"} TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"} +TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"} TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"} TS_CMD_FALLOCATE=${TS_CMD_FALLOCATE-"${ts_commandsdir}fallocate"} TS_CMD_FDISK=${TS_CMD_FDISK-"${ts_commandsdir}fdisk"} diff --git a/tests/expected/misc/enosys-basic b/tests/expected/misc/enosys-basic new file mode 100644 index 000000000..6552946f5 --- /dev/null +++ b/tests/expected/misc/enosys-basic @@ -0,0 +1,4 @@ +test_enosys: fallocate r=-1 errno=Bad file descriptor +test_enosys: fallocate r=-1 errno=Function not implemented +test_enosys: fallocate r=-1 errno=Function not implemented +test_enosys: fallocate r=-1 errno=Function not implemented diff --git a/tests/expected/misc/enosys-exec b/tests/expected/misc/enosys-exec new file mode 100644 index 000000000..be9e72f3d --- /dev/null +++ b/tests/expected/misc/enosys-exec @@ -0,0 +1 @@ +test_enosys: exec failed: Function not implemented diff --git a/tests/helpers/test_enosys.c b/tests/helpers/test_enosys.c index 88e7af3b0..69f7af9af 100644 --- a/tests/helpers/test_enosys.c +++ b/tests/helpers/test_enosys.c @@ -16,128 +16,35 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <stddef.h> -#include <stdbool.h> -#include <getopt.h> - -#include <linux/unistd.h> -#include <linux/filter.h> -#include <linux/seccomp.h> -#include <linux/audit.h> -#include <sys/prctl.h> - -#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 }, -}; +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> int main(int argc, char **argv) { - int c; - size_t i; - bool found; - static const struct option longopts[] = { - { "syscall", required_argument, NULL, 's' }, - { 0 } - }; + int r; - 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 (argc != 2) { + fprintf(stderr, "invalid options\n"); + return EXIT_FAILURE; } - 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; - } + if (strcmp(argv[1], "fallocate") == 0) { + errno = 0; + r = fallocate(-1, 0, 0, 0); + errx(EXIT_SUCCESS, "fallocate r=%d errno=%s", r, strerror(errno)); + } else if (strcmp(argv[1], "exec") == 0) { + char *const cmd[] = { + "/bin/false", + NULL + }; + execve(cmd[0], cmd, NULL); + err(EXIT_FAILURE, "exec failed"); } - struct sock_fprog prog = { - .len = ARRAY_SIZE(filter), - .filter = filter, - }; - - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) - err(EXIT_NOTSUPP, "prctl(PR_SET_NO_NEW_PRIVS)"); - - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) - err(EXIT_NOTSUPP, "prctl(PR_SET_SECCOMP)"); - - if (execvp(argv[optind], argv + optind)) - err(EXIT_NOTSUPP, "Could not exec"); + errx(EXIT_FAILURE, "invalid mode %s", argv[1]); } diff --git a/tests/ts/misc/enosys b/tests/ts/misc/enosys new file mode 100755 index 000000000..2a8241296 --- /dev/null +++ b/tests/ts/misc/enosys @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright (C) 2022 Thomas Weißschuh <thomas@t-8ch.de> +# +# This file is part of util-linux. +# +# This file 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 file is distributed in the hope that it will 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. + +TS_TOPDIR="${0%/*}/../.." +TS_DESC="enosys" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" + +ts_check_test_command "$TS_CMD_ENOSYS" +ts_check_test_command "$TS_HELPER_ENOSYS" + +"$TS_CMD_ENOSYS" true 2> /dev/null +[ "$?" -eq "$TS_EXIT_NOTSUPP" ] && ts_skip "enosys does not work" + +ts_init_subtest basic + +FALLOCATE_TEST="$TS_HELPER_ENOSYS fallocate" + +$FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT" +"$TS_CMD_ENOSYS" -s fallocate $FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT" +"$TS_CMD_ENOSYS" -s fsopen -s fallocate $FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT" +"$TS_CMD_ENOSYS" -s fallocate -s fsopen $FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT" + +ts_finalize_subtest + +ts_init_subtest exec + +FALLOCATE_TEST="$TS_HELPER_ENOSYS exec" + +$FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT" +"$TS_CMD_ENOSYS" -s execve $FALLOCATE_TEST > /dev/null 2>> "$TS_OUTPUT" + +ts_finalize_subtest + +ts_finalize diff --git a/tests/ts/mount/fallback b/tests/ts/mount/fallback index fe932ee78..6033eb575 100755 --- a/tests/ts/mount/fallback +++ b/tests/ts/mount/fallback @@ -6,15 +6,15 @@ TS_DESC="fstab-fallback" . "$TS_TOPDIR"/functions.sh ts_init "$*" -ts_check_test_command "$TS_HELPER_ENOSYS" +ts_check_test_command "$TS_CMD_ENOSYS" ts_check_test_command "$TS_CMD_MOUNT" ts_check_test_command "$TS_CMD_UMOUNT" ts_check_test_command "$TS_CMD_FINDMNT" ts_check_test_command "$TS_CMD_LOSETUP" ts_skip_nonroot -"$TS_HELPER_ENOSYS" true 2> /dev/null -[ "$?" -eq "$TS_EXIT_NOTSUPP" ] && ts_skip "test_enosys does not work" +"$TS_CMD_ENOSYS" true 2> /dev/null +[ "$?" -eq "$TS_EXIT_NOTSUPP" ] && ts_skip "enosys does not work" test_mount_fallback() { ts_init_subtest "$1" @@ -24,7 +24,7 @@ test_mount_fallback() { mkdir -p "$MY_SOURCE" mkdir -p "$TS_MOUNTPOINT" - "$TS_HELPER_ENOSYS" $2 -- "$TS_CMD_MOUNT" --bind "$MY_SOURCE" "$TS_MOUNTPOINT" \ + "$TS_CMD_ENOSYS" $2 -- "$TS_CMD_MOUNT" --bind "$MY_SOURCE" "$TS_MOUNTPOINT" \ >> "$TS_OUTPUT" 2>> "$TS_ERRLOG" [ "$?" = "0" ] || ts_log "error: mount $TS_MOUNTPOINT" @@ -51,7 +51,7 @@ mkdir -p "$MOUNTPOINT" ts_init_subtest "later-fsopen" -$TS_HELPER_ENOSYS -s fsopen -- "$TS_CMD_MOUNT" -t foo,bar,ext2 "$DEVICE" "$MOUNTPOINT" \ +$TS_CMD_ENOSYS -s fsopen -- "$TS_CMD_MOUNT" -t foo,bar,ext2 "$DEVICE" "$MOUNTPOINT" \ >> $TS_OUTPUT 2>> $TS_ERRLOG ts_is_mounted $DEVICE || ts_log "Cannot find $DEVICE in /proc/mounts" mkdir -p ${MOUNTPOINT}/subdir @@ -60,7 +60,7 @@ ts_finalize_subtest ts_init_subtest "subdir" -$TS_HELPER_ENOSYS -s fsopen -s open_tree -- \ +$TS_CMD_ENOSYS -s fsopen -s open_tree -- \ "$TS_CMD_MOUNT" -o X-mount.subdir=subdir "$DEVICE" "$MOUNTPOINT" \ >> $TS_OUTPUT 2>> $TS_ERRLOG ts_is_mounted $DEVICE || ts_log "Cannot find $DEVICE in /proc/mounts" diff --git a/tools/Makemodule.am b/tools/Makemodule.am index dbda62587..ebee6c334 100644 --- a/tools/Makemodule.am +++ b/tools/Makemodule.am @@ -56,4 +56,6 @@ EXTRA_DIST += \ tools/config-gen.d/non-libmount.conf \ \ tools/asciidoctor-includetracker.rb \ - tools/asciidoctor-unicodeconverter.rb + tools/asciidoctor-unicodeconverter.rb \ + \ + tools/all_syscalls diff --git a/tools/all_syscalls b/tools/all_syscalls new file mode 100755 index 000000000..9c147786c --- /dev/null +++ b/tools/all_syscalls @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +OUTPUT=syscalls.h +SYSCALL_INCLUDES=" +#include <sys/syscall.h> +" + +trap 'rm $OUTPUT $OUTPUT.deps' ERR + +"$@" -MD -MF "$OUTPUT.deps" <<< "$SYSCALL_INCLUDES" -dM -E - \ + | gawk 'match($0, /^#define __NR_([^ ]+)/, res) { print "UL_SYSCALL(\"" res[1] "\", __NR_" res[1] ")" }' \ + | sort \ + > "$OUTPUT" |