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
This commit is contained in:
dongshengyuan
2026-08-12 03:11:02 +09:00
committed by Yu Watanabe
parent e8454eb14c
commit e98c68104b
2 changed files with 26 additions and 1 deletions
+6 -1
View File
@@ -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;
+20
View File
@@ -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"