7

I'm implementing features of an ssh server, so given a shell request I open a pty-tty pair.
A snippet:

import (
    "github.com/creack/pty"
    ...
)

func attachPty(channel ssh.Channel, shell *exec.Cmd) {
    mypty, err := pty.Start(shell)
    go func() {
        io.Copy(channel, mypty) // (1) ; could also be substituted with read() syscall, same problem
    }
    go func() {
        io.Copy(mypty, channel) // (2) - this returns on channel exit with eof, so let's close mypty
        if err := syscall.Close(int(mypty.Fd())); err != nil {
            fmt.Printf("error closing fd") // no error is printed out, /proc/fd shows it's successfuly closed
        }
    }
}

Once the ssh channel gets closed, I close the pty. My expected behavior is that it should send SIGHUP to the shell.

If I comment out the (1) copy (src: mypty, dst: channel), it works!
However - when it's not commented out:

  • The (1) copy doesn't return, meaning the read syscall from mypty is still blocking, and doesn't return eof => master device doesn't get closed?
  • shell doesn't get SIGHUP

I'm not sure why if I comment out the (1) copy it works, maybe the kernel reference counts the reads?

My leads:

Go notes:

  • I close the fd directly, because otherwise using the usual os.File.close() doesn't actually close the fd for some reason, it stays open in /proc/<pid>/fd

  • substituting the (1) copy with a direct read syscall would lead to the same outcome

Thank you!

Noam Solovechick
  • 1,127
  • 2
  • 15
  • 29

1 Answers1

0

Ran into this myself and spent some time investigating.

Turns out that this is deliberate behaviour of Go runtime. Specifically it prevents files from being closed while they are being read by another goroutine. This was introduced to mitigate a race condition where a goroutine may read from a wrong file if the file descriptor was closed and reused by another goroutine.

I don't have a good solution for the SSHD use case. I may update this answer later. Have you come up with some workaround yourself?

Nicht Verstehen
  • 421
  • 2
  • 11