From e98c68104b15459be34c67af54ce736fd3bff3a5 Mon Sep 17 00:00:00 2001 From: dongshengyuan <545258830@qq.com> Date: Mon, 10 Aug 2026 09:59:40 +0800 Subject: [PATCH] core: do not chown fd-passed TTYs When stdio is passed as file descriptors, the provider of those descriptors remains responsible for their ownership and access mode. This keeps run0 in a pipeline from changing the caller's TTY ownership to the target user, which can otherwise break later terminal access from the original user. Fixes #43336 --- src/core/exec-invoke.c | 7 ++++++- test/units/TEST-74-AUX-UTILS.run.sh | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index ae3001418a..f54caf44ce 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -5722,7 +5722,12 @@ int exec_invoke( } #endif - if (uid_is_valid(uid)) { + /* Only adjust ownership for TTYs we acquired via StandardInput=tty*. If stdin is passed + * as an fd, its ownership is managed by the provider of the fd, see exec_context_revert_tty(). */ + if (uid_is_valid(uid) && + params->stdin_fd < 0 && + exec_input_is_terminal(context->std_input) && + exec_context_tty_path(context)) { r = chown_terminal(STDIN_FILENO, uid); if (r < 0) { *exit_status = EXIT_STDIN; diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh index 1c0efdf08f..53abc55efa 100755 --- a/test/units/TEST-74-AUX-UTILS.run.sh +++ b/test/units/TEST-74-AUX-UTILS.run.sh @@ -339,6 +339,26 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the assert_eq "$(run0 --pipe echo -n foo)" "foo" assert_eq "$(run0 --pipe --pty echo -n foo)" "foo" + # When run0 is used in a pipeline, the caller's TTY is passed through as an fd. Its + # ownership/access mode must not be changed to the target user. + script -qec 'bash -euxc '"'"' + set -o pipefail + tty_path="$(tty)" + tty_stat="$(stat -Lc "%u:%g:%a" "$tty_path")" + run0 --user=testuser echo -n foo | cat >/dev/null + [[ "$(stat -Lc "%u:%g:%a" "$tty_path")" == "$tty_stat" ]] + '"'" /dev/null + + # --pty also passes a service PTY as an fd. It must still provide a usable TTY to + # the target user without changing the caller's TTY ownership/access mode. + script -qec 'bash -euxc '"'"' + tty_path="$(tty)" + tty_stat="$(stat -Lc "%u:%g:%a" "$tty_path")" + run0 --pty --user=testuser bash -xec \ + "test \"\$(id -un)\" = testuser; test -t 0; test -t 1; test -t 2; tty >/dev/null" + [[ "$(stat -Lc "%u:%g:%a" "$tty_path")" == "$tty_stat" ]] + '"'" /dev/null + # Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not assert_neq "$(run0 --pty tty < /dev/null)" "not a tty" assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty"