From 846cafe9ae25fdf6b06fbd896a533a035ce4e9aa Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 3 Nov 2026 13:09:41 +0100 Subject: [PATCH] analyze: add io-uring-filter verb IOUringFilter= takes the names of io_uring operations, and SystemCallFilter= denies an operation if it denies one of the system calls that the operation can do the work of. Users can only find the operation names and the system calls of each operation in the source code. Let's add "systemd-analyze io-uring-filter", which lists the operations that systemd knows, together with the system calls of each operation, like "systemd-analyze syscall-filter" does for the system call groups. --- man/systemd-analyze.xml | 31 +++++++++++ man/systemd.exec.xml | 6 ++- shell-completion/bash/systemd-analyze | 12 +++++ shell-completion/zsh/_systemd-analyze | 8 +++ src/analyze/analyze-io-uring-filter.c | 76 +++++++++++++++++++++++++++ src/analyze/analyze-io-uring-filter.h | 6 +++ src/analyze/analyze.c | 7 ++- src/analyze/meson.build | 1 + test/units/TEST-65-ANALYZE.sh | 4 ++ 9 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 src/analyze/analyze-io-uring-filter.c create mode 100644 src/analyze/analyze-io-uring-filter.h diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index e77f8ad62a2..084ababf9a3 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -116,6 +116,12 @@ syscall-filter SET… + + systemd-analyze + OPTIONS + io-uring-filter + NAME… + systemd-analyze OPTIONS @@ -600,6 +606,31 @@ Conditions succeeded. SET must include the @ prefix. + + <command>systemd-analyze io-uring-filter <optional><replaceable>NAME</replaceable>...</optional></command> + + Lists the + io_uring7 + operations that systemd knows, or only the operations with the specified names. The names are the ones + that IOUringFilter= takes, see + systemd.exec5. For + each operation, the SYSCALLS column lists the system calls whose work the operation + can do. SystemCallFilter= denies an operation if it denies any of the listed system + calls. + + + Table output + $ systemd-analyze io-uring-filter nop read socket uring_cmd +NAME SYSCALLS +nop - +read read pread64 +socket socket +uring_cmd ioctl setsockopt getsockopt getsockname + + + + + <command>systemd-analyze filesystems <optional><replaceable>SET</replaceable>...</optional></command> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 7d557e45945..69649f7f9b9 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -2539,7 +2539,8 @@ BindReadOnlyPaths=/var/lib/systemd listed operations are denied (deny-listing). Otherwise, only the listed operations are allowed (allow-listing). An allow list also denies the operations of newer kernels that systemd doesn't know, even though the kernel reports them as supported. Operation names that systemd doesn't know - are ignored. + are ignored. Use systemd-analyze io-uring-filter to list the operations that + systemd knows. This option may be specified more than once, in which case the lists are merged. If the first assignment starts with ~, the list is a deny list, otherwise it is an allow list. @@ -3043,7 +3044,8 @@ RestrictNamespaces=~cgroup net if the filter allows io_uring_setup(). An operation that can do the work of one or more system calls, such as IORING_OP_OPENAT or IORING_OP_SOCKET, is denied if the filter denies any of its equivalent system - calls. Operations of newer kernels that systemd doesn't know are not affected. Denied requests + calls. systemd-analyze io-uring-filter lists the equivalent system calls of each + operation. Operations of newer kernels that systemd doesn't know are not affected. Denied requests always fail with EACCES, regardless of SystemCallErrorNumber= and of the action of the system call. System calls that are denied with an error number of 0 do not affect io_uring operations, since a denied request diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze index 719c3f53918..dca628a48b8 100644 --- a/shell-completion/bash/systemd-analyze +++ b/shell-completion/bash/systemd-analyze @@ -52,6 +52,10 @@ __get_syscall_sets() { done } +__get_io_uring_ops() { + systemd-analyze --no-legend --no-pager io-uring-filter 2>/dev/null | { while read -r a b; do echo " $a"; done; } +} + __get_architectures() { systemd-analyze --no-legend --no-pager architectures 2>/dev/null | { while read -r a b; do echo " $a"; done; } } @@ -88,6 +92,7 @@ _systemd_analyze() { [DUMP]='dump' [VERIFY]='verify' [SECCOMP_FILTER]='syscall-filter' + [IO_URING_FILTER]='io-uring-filter' [CAT_CONFIG]='cat-config' [SECURITY]='security' [CONDITION]='condition' @@ -249,6 +254,13 @@ _systemd_analyze() { comps='--help --version --system --user --global --no-pager --json=off --json=pretty --json=short --table --no-legend --scale-svg --detailed' fi + elif __contains_word "$verb" ${VERBS[IO_URING_FILTER]}; then + if [[ $cur = -* ]]; then + comps='--help --version --no-pager --json=off --json=pretty --json=short --no-legend' + else + comps=$( __get_io_uring_ops ) + fi + elif __contains_word "$verb" ${VERBS[ARCHITECTURES]}; then if [[ $cur = -* ]]; then comps='--help --version --no-pager --json=off --json=pretty --json=short --no-legend' diff --git a/shell-completion/zsh/_systemd-analyze b/shell-completion/zsh/_systemd-analyze index e6dbbcde400..e91f47966a9 100644 --- a/shell-completion/zsh/_systemd-analyze +++ b/shell-completion/zsh/_systemd-analyze @@ -43,6 +43,13 @@ _describe -t groups 'syscall groups' _groups || compadd "$@" } +(( $+functions[_systemd-analyze_io-uring-filter] )) || + _systemd-analyze_io-uring-filter() { + local -a _ops + _ops=( $(systemd-analyze --quiet --no-pager --no-legend io-uring-filter 2>/dev/null | { while read -r a b; do echo " $a"; done; }) ) + _describe -t operations 'io_uring operations' _ops || compadd "$@" + } + (( $+functions[_systemd-analyze_filesystems] )) || _systemd-analyze_filesystems() { local -a _groups @@ -95,6 +102,7 @@ 'exit-status:List known exit statuses' 'capability:List capability definitions' 'syscall-filter:List syscalls in seccomp filters' + 'io-uring-filter:List io_uring operations' 'filesystems:List known filesystems' 'condition:Evaluate Condition*= and Assert*= assignments' 'verify:Check unit files for correctness' diff --git a/src/analyze/analyze-io-uring-filter.c b/src/analyze/analyze-io-uring-filter.c new file mode 100644 index 00000000000..96a46e3de7a --- /dev/null +++ b/src/analyze/analyze-io-uring-filter.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "analyze.h" +#include "analyze-io-uring-filter.h" +#include "format-table.h" +#include "io-uring-filter.h" +#include "log.h" +#include "nulstr-util.h" +#include "strv.h" + +static int add_op(Table *t, int op) { + _cleanup_strv_free_ char **syscalls = NULL; + const char *s; + int r; + + assert(t); + + s = io_uring_op_syscalls_to_string(op); + if (s) { + syscalls = strv_split_nulstr(s); + if (!syscalls) + return log_oom(); + } + + r = table_add_many(t, + TABLE_INT, op, + TABLE_STRING, io_uring_op_to_string(op), + TABLE_STRV_WRAPPED, syscalls); + if (r < 0) + return table_log_add_error(r); + + return 0; +} + +int verb_io_uring_filter(int argc, char *argv[], uintptr_t _data, void *userdata) { + _cleanup_(table_unrefp) Table *table = NULL; + int r; + + table = table_new("opcode", "name", "syscalls"); + if (!table) + return log_oom(); + + (void) table_hide_column_from_display(table, (size_t) 0); + table_set_ersatz_string(table, TABLE_ERSATZ_DASH); + + char **args = strv_skip(argv, 1); + if (args) { + STRV_FOREACH(arg, args) { + int op; + + op = io_uring_op_from_string(*arg); + if (op < 0) + return log_error_errno(op, "io_uring operation \"%s\" not known.", *arg); + + r = add_op(table, op); + if (r < 0) + return r; + } + + (void) table_set_sort(table, (size_t) 0); + } else + for (int op = 0; op < IORING_OP_LAST; op++) { + r = add_op(table, op); + if (r < 0) + return r; + } + + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); + if (r < 0) + return r; + + return EXIT_SUCCESS; +} diff --git a/src/analyze/analyze-io-uring-filter.h b/src/analyze/analyze-io-uring-filter.h new file mode 100644 index 00000000000..3bd41b2bcd7 --- /dev/null +++ b/src/analyze/analyze-io-uring-filter.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "forward.h" + +int verb_io_uring_filter(int argc, char *argv[], uintptr_t _data, void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 71a8a9ea3f4..083854b1290 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -31,6 +31,7 @@ #include "analyze-has-tpm2.h" #include "analyze-image-policy.h" #include "analyze-inspect-elf.h" +#include "analyze-io-uring-filter.h" #include "analyze-log-control.h" #include "analyze-malloc.h" #include "analyze-nvpcrs.h" @@ -241,6 +242,8 @@ VERB_SCOPE(, verb_capabilities, "capability", "[CAP...]\0", VERB_ANY, VERB_ANY, "List capability definitions"); VERB_SCOPE(, verb_syscall_filters, "syscall-filter", "[NAME...]\0", VERB_ANY, VERB_ANY, 0, "List syscalls in seccomp filters"); +VERB_SCOPE(, verb_io_uring_filter, "io-uring-filter", "[NAME...]\0", VERB_ANY, VERB_ANY, 0, + "List io_uring operations"); VERB_SCOPE(, verb_filesystems, "filesystems", "[NAME...]\0", VERB_ANY, VERB_ANY, 0, "List known filesystems"); VERB_SCOPE(, verb_architectures, "architectures", "[NAME...]\0", VERB_ANY, VERB_ANY, 0, @@ -587,9 +590,9 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) { !STRPTR_IN_SET(verb, "security", "inspect-elf", "dlopen-metadata", "plot", "fdstore", "pcrs", "nvpcrs", "architectures", "cpu-features", "capability", "exit-status", "chid", "blame", - "identify-tpm2", "restart-delays")) + "identify-tpm2", "restart-delays", "io-uring-filter")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Option --json= is only supported for security, inspect-elf, dlopen-metadata, plot, fdstore, pcrs, nvpcrs, architectures, cpu-features, capability, exit-status, chid, blame, identify-tpm2, restart-delays right now."); + "Option --json= is only supported for security, inspect-elf, dlopen-metadata, plot, fdstore, pcrs, nvpcrs, architectures, cpu-features, capability, exit-status, chid, blame, identify-tpm2, restart-delays, io-uring-filter right now."); if (arg_threshold != 100 && !streq_ptr(verb, "security")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/src/analyze/meson.build b/src/analyze/meson.build index 3ba3b354707..9f874ba92c4 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -20,6 +20,7 @@ systemd_analyze_sources = files( 'analyze-has-tpm2.c', 'analyze-image-policy.c', 'analyze-inspect-elf.c', + 'analyze-io-uring-filter.c', 'analyze-log-control.c', 'analyze-malloc.c', 'analyze-nvpcrs.c', diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index 0e91c76dc58..8a296bea845 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -150,6 +150,10 @@ systemd-analyze syscall-filter @chown @sync systemd-analyze syscall-filter @sync @sync @sync (! systemd-analyze syscall-filter @chown @sync @foobar) (! systemd-analyze syscall-filter --global) +systemd-analyze io-uring-filter >/dev/null +systemd-analyze io-uring-filter nop read uring_cmd +systemd-analyze io-uring-filter --json=short socket | jq -e '.[0] == {"name": "socket", "syscalls": ["socket"]}' +(! systemd-analyze io-uring-filter read foobar) # filesystems (requires libbpf support) if systemctl --version | grep "+BPF_FRAMEWORK"; then systemd-analyze filesystems >/dev/null