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: 57c909dab1

* Implement dup2 with syscall.Dup3 on all linux platforms

* Add explicit "unix" constraint to dup2_unix.go to ensure Windows is excluded
This commit is contained in:
Cam Hutchison
2025-06-23 10:45:06 +00:00
committed by GitHub
parent 68b82698bc
commit b3a3066819
3 changed files with 68 additions and 5 deletions
+34
View File
@@ -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 <http://www.gnu.org/licenses/>.
//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)
}
+28
View File
@@ -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 <http://www.gnu.org/licenses/>.
//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)
}
+6 -5
View File
@@ -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)
}