From b3a3066819e23258a90ef7a7a194b7d370b3f1f7 Mon Sep 17 00:00:00 2001 From: Cam Hutchison Date: Mon, 23 Jun 2025 20:45:06 +1000 Subject: [PATCH] tsh: Add wrapper for syscall.Dup2 for linux/arm64 (#55925) * tsh: Add wrapper for syscall.Dup2 for linux/arm64 Add a wrapper for `syscall.Dup2()` as linux ARM64 does not have that syscall. On that platform, `syscall.Dup3()` needs to be used instead. Fixes: 57c909dab1b8f7f0a8ba7e3199a902775f82f42a * Implement dup2 with syscall.Dup3 on all linux platforms * Add explicit "unix" constraint to dup2_unix.go to ensure Windows is excluded --- tool/tsh/common/dup2_linux.go | 34 ++++++++++++++++++++++++++++++++++ tool/tsh/common/dup2_unix.go | 28 ++++++++++++++++++++++++++++ tool/tsh/common/reexec_unix.go | 11 ++++++----- 3 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 tool/tsh/common/dup2_linux.go create mode 100644 tool/tsh/common/dup2_unix.go diff --git a/tool/tsh/common/dup2_linux.go b/tool/tsh/common/dup2_linux.go new file mode 100644 index 00000000000..52ca8b0585e --- /dev/null +++ b/tool/tsh/common/dup2_linux.go @@ -0,0 +1,34 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +//go:build linux + +package common + +import "syscall" + +// dup2 implements syscall.Dup2(oldfd, newfd) in a way that works on all +// current Linux platforms, and likely on any new platforms. New platforms +// such as ARM64 do not implement syscall.Dup2() instead implementing +// syscall.Dup3() which is largely a superset, with one special case. +func dup2(oldfd, newfd int) error { + if oldfd == newfd { + // dup2 would do nothing in this case, but dup3 returns an error. + // Emulate dup2 behavior. + return nil + } + return syscall.Dup3(oldfd, newfd, 0) +} diff --git a/tool/tsh/common/dup2_unix.go b/tool/tsh/common/dup2_unix.go new file mode 100644 index 00000000000..47aef968848 --- /dev/null +++ b/tool/tsh/common/dup2_unix.go @@ -0,0 +1,28 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +//go:build unix && !linux + +package common + +import "syscall" + +// dup2 wraps syscall.Dup2(oldfd, newfd) on non-linux unix platforms. The +// linux implementation uses syscall.Dup3() as Dup2() is not available +// on all linux platforms. +func dup2(oldfd, newfd int) error { + return syscall.Dup2(oldfd, newfd) +} diff --git a/tool/tsh/common/reexec_unix.go b/tool/tsh/common/reexec_unix.go index bbcf9a0d5a6..4a707ab8a62 100644 --- a/tool/tsh/common/reexec_unix.go +++ b/tool/tsh/common/reexec_unix.go @@ -51,16 +51,17 @@ func replaceStdin() (*os.File, error) { } var dupErr error if ctrlErr := rc.Control(func(fd uintptr) { - dupErr = syscall.Dup2(int(fd), syscall.Stdin) - // stdin is not O_CLOEXEC after dup2 but thankfully the three stdio - // file descriptors must be not O_CLOEXEC anyway, so we can avoid - // a linux-specific implementation or syscall.ForkLock shenanigans + dupErr = dup2(int(fd), syscall.Stdin) + // dup2() is sufficient here as the three stdio file + // descriptors must not be O_CLOEXEC. Darwin does not have + // dup3(), so would need to resort to syscall.ForkLock + // shenanigans if we did need to set O_CLOEXEC. }); ctrlErr != nil { _ = devNull.Close() return nil, trace.Wrap(ctrlErr) } if dupErr != nil { - // this is the error from Dup2 + // this is the error from dup2 _ = devNull.Close() return nil, trace.Wrap(err) }