summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2023-05-17 13:34:34 +0200
committerKarel Zak <kzak@redhat.com>2023-05-17 13:34:34 +0200
commitbd2ebbd4eadd29efac994037c0e85e8ddad13248 (patch)
treec093e2c8e59a28d2eaf0cc5e921ea07e7a8970a0
parent372e026e09382dae5385718567ecd97ad0637ece (diff)
parentafb669afededdc2865bc132b41a4d70b8c2ee840 (diff)
downloadutil-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.sh1
-rw-r--r--bash-completion/Makemodule.am3
-rw-r--r--bash-completion/enosys37
-rw-r--r--configure.ac4
-rw-r--r--include/c.h3
-rw-r--r--meson.build19
-rw-r--r--misc-utils/Makemodule.am18
-rw-r--r--misc-utils/enosys.1.adoc60
-rw-r--r--misc-utils/enosys.c204
-rw-r--r--misc-utils/waitpid.c3
-rw-r--r--tests/commands.sh1
-rw-r--r--tests/expected/misc/enosys-basic4
-rw-r--r--tests/expected/misc/enosys-exec1
-rw-r--r--tests/helpers/test_enosys.c139
-rwxr-xr-xtests/ts/misc/enosys49
-rwxr-xr-xtests/ts/mount/fallback12
-rw-r--r--tools/Makemodule.am4
-rwxr-xr-xtools/all_syscalls15
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"