Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ioctl tty TCSETSF unsupported. Python tty.setraw() fails #9335

Closed
thundergolfer opened this issue Sep 4, 2023 · 3 comments · Fixed by #9339
Closed

ioctl tty TCSETSF unsupported. Python tty.setraw() fails #9335

thundergolfer opened this issue Sep 4, 2023 · 3 comments · Fixed by #9339
Assignees
Labels
type: bug Something isn't working

Comments

@thundergolfer
Copy link
Contributor

thundergolfer commented Sep 4, 2023

Description

The TCSETSF argument is unimplemented in https://github.com/google/gvisor/blob/master/pkg/sentry/fsimpl/devpts/master.go#L221, causing https://docs.python.org/3/library/tty.html#tty.setraw to fail because that function by default uses TCSETSF.

I0903 01:29:39.651779  1196665 strace.go:567] [   2:   2] python E ioctl(0x0 /dev/pts/0, 0x5401, 0x7ea66e856fe0)
I0903 01:29:39.651792  1196665 strace.go:605] [   2:   2] python X ioctl(0x0 /dev/pts/0, 0x5401, 0x7ea66e856fe0) = 0 (0x0) (1.778µs)
I0903 01:29:39.651803  1196665 strace.go:567] [   2:   2] python E ioctl(0x0 /dev/pts/0, 0x5404, 0x7ea66e856fb0)
I0903 01:29:39.651821  1196665 compat.go:120] Unsupported syscall ioctl(0x0,0x5404,0x7ea66e856fb0,0x0,0x10,0x7ea66e857054). It is likely that you can safely ignore this message and that 
this is not the cause of any error. Please, refer to https://gvisor.dev/c/linux/amd64/ioctl for more information.
I0903 01:29:39.651831  1196665 strace.go:605] [   2:   2] python X ioctl(0x0 /dev/pts/0, 0x5404, 0x7ea66e856fb0) = 0 (0x0) errno=25 (not a typewriter) (22.458µs)

The workaround in our case is simple, just doing tty.setraw(pty.STDIN_FILENO, when=termios.TCSANOW), but it seems like created a compatibility-tagged issue is the thing to do. I'd also venture that this page should have a "Partially supported" entry for ioctl_tty: https://gvisor.dev/docs/user_guide/compatibility/linux/amd64/

Steps to reproduce

A modified Python pty.spawn function can be used to surface the issue:

# mod_spawn.py
import pty

def spawn(argv, master_read=pty._read, stdin_read=pty._read):
    """Create a spawned process. Modified from pty.spawn"""
    if type(argv) == type(''):
        argv = (argv,)
    sys.audit('pty.spawn', argv)
    pid, master_fd = pty.fork()
    if pid == pty.CHILD:
        os.execlp(argv[0], *argv)
    try:
        mode = tty.tcgetattr(pty.STDIN_FILENO)
        print("set raw")
        tty.setraw(pty.STDIN_FILENO)
        restore = 1
    except tty.error:    # This is the same as termios.error
        raise
        restore = 0
    try:
        pty._copy(master_fd, master_read, stdin_read)
    except OSError:
        if restore:
            tty.tcsetattr(pty.STDIN_FILENO, tty.TCSAFLUSH, mode)

    os.close(master_fd)
    return os.waitpid(pid, 0)[1]

if __name__ == "__main__":
    pty.spawn("sh")

The stdlib implementation ignores the exception.

docker run -it --runtime=runsc mod1323 python3 -c "$(cat mod_spawn.py)" does not reproduce because the ioctl is made to the host. The ioctl only fails when issued to /dev/pts/0.

To give a proper reproduction I'd have to extract out some of our code, which I don't think is necessary because it's clear that the ioctl cmd is unimplemented.

runsc version

runsc version release-20230717.0-12-g0244c8c19fb7
spec: 1.1.0-rc.1

docker version (if using docker)

Client: Docker Engine - Community
 Version:           23.0.1
 API version:       1.42
 Go version:        go1.19.5
 Git commit:        a5ee5b1
 Built:             Thu Feb  9 19:46:56 2023
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          23.0.1
  API version:      1.42 (minimum version 1.12)
  Go version:       go1.19.5
  Git commit:       bc3805a
  Built:            Thu Feb  9 19:46:56 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.18
  GitCommit:        2456e983eb9e37e47538f59ea18f2043c9a73640
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

uname

Linux ip-172-31-70-51 5.15.0-1039-aws #44~20.04.1-Ubuntu SMP Thu Jun 22 12:21:12 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

kubectl (if using Kubernetes)

No response

repo state (if built from source)

No response

runsc debug logs (if available)

I0903 19:41:25.470943  1419149 strace.go:605] [   5:   5] python X ioctl(0x0 /dev/pts/1, 0x5413, 0x7eb4f3d3a758) = 0 (0x0) (3.263µs)
I0903 19:41:25.470947  1419149 strace.go:567] [   2:   2] python E write(0x1 host:[3], 0x7ec8bb557b30 "set raw ok", 0xa)
I0903 19:41:25.470931  1419149 strace.go:614] [   2:   3] python X futex(0x7ec8bdb9d10c, FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG, 0x0, 0x7ec8bb3fdea0 {sec=1 nsec=278223718}, 0x0, 0xffffffff) = 0 (0x0) (432.032µs)
I0903 19:41:25.470964  1419149 strace.go:605] [   2:   2] python X write(0x1 host:[3], ..., 0xa) = 10 (0xa) (10.047µs)
I0903 19:41:25.470967  1419149 strace.go:576] [   2:   3] python E futex(0x7ec8bdb9d110, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 0x1, null, 0x0, 0x0)
I0903 19:41:25.470975  1419149 strace.go:567] [   5:   5] python E ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a0a0)
I0903 19:41:25.470985  1419149 strace.go:605] [   5:   5] python X ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a0a0) = 0 (0x0) (1.351µs)
I0903 19:41:25.470988  1419149 strace.go:576] [   2:   2] python E futex(0x7ec8bdb9d108, FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG, 0x0, 0x7eb4f3d3aff0 {sec=1 nsec=278734418}, 0x0, 0xffffffff)
I0903 19:41:25.470976  1419149 strace.go:614] [   2:   3] python X futex(0x7ec8bdb9d110, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 0x1, null, 0x0, 0x0) = 0 (0x0) (439ns)
I0903 19:41:25.471007  1419149 strace.go:567] [   5:   5] python E ioctl(0x0 /dev/pts/1, 0x5413, 0x7eb4f3d3a578)
I0903 19:41:25.471265  1419149 strace.go:605] [   5:   5] python X ioctl(0x0 /dev/pts/1, 0x5413, 0x7eb4f3d3a578) = 0 (0x0) (4.007µs)
I0903 19:41:25.471279  1419149 strace.go:559] [   2:   3] python E getpid()
I0903 19:41:25.471291  1419149 strace.go:596] [   2:   3] python X getpid() = 2 (0x2) (284ns)
I0903 19:41:25.471309  1419149 strace.go:567] [   5:   5] python E ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a640)
I0903 19:41:25.471314  1419149 strace.go:605] [   5:   5] python X ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a640) = 0 (0x0) (745ns)
I0903 19:41:25.471325  1419149 strace.go:559] [   2:   3] python E getpid()
I0903 19:41:25.471338  1419149 strace.go:596] [   2:   3] python X getpid() = 2 (0x2) (475ns)
I0903 19:41:25.471768  1419149 strace.go:576] [   2:   3] python E futex(0x7ec8bdb9d108, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 0x1, null, 0x7ec8bdb9d0e0, 0x7ec8bdb9d100)
I0903 19:41:25.471781  1419149 strace.go:614] [   2:   3] python X futex(0x7ec8bdb9d108, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 0x1, null, 0x7ec8bdb9d0e0, 0x7ec8bdb9d100) = 1 (0x1) (1.435µs)
I0903 19:41:25.471790  1419149 strace.go:614] [   2:   2] python X futex(0x7ec8bdb9d108, FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG, 0x0, 0x7eb4f3d3aff0 {sec=1 nsec=278734418}, 0x0, 0xffffffff) = 0 (0x0) (793.426µs)
I0903 19:41:25.471800  1419149 strace.go:570] [   2:   3] python E epoll_wait(0x3 anon_inode:[eventpoll], 0x7ec8bb65d990 {}, 0x2, 0x3a3a)
I0903 19:41:25.471810  1419149 strace.go:576] [   2:   2] python E futex(0x7ec8bdb9d110, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 0x1, null, 0x0, 0x0)
I0903 19:41:25.471815  1419149 strace.go:614] [   2:   2] python X futex(0x7ec8bdb9d110, FUTEX_WAKE|FUTEX_PRIVATE_FLAG, 0x1, null, 0x0, 0x0) = 0 (0x0) (273ns)
I0903 19:41:25.471832  1419149 strace.go:567] [   2:   2] python E write(0x1 host:[3], 0x7ec8bb717a70 "\n", 0x1)
I0903 19:41:25.471846  1419149 strace.go:605] [   2:   2] python X write(0x1 host:[3], ..., 0x1) = 1 (0x1) (8.417µs)
I0903 19:41:25.471953  1419149 strace.go:567] [   2:   2] python E ioctl(0x0 /dev/pts/0, 0x5401, 0x7eb4f3d3b010)
I0903 19:41:25.471963  1419149 strace.go:605] [   2:   2] python X ioctl(0x0 /dev/pts/0, 0x5401, 0x7eb4f3d3b010) = 0 (0x0) (1.274µs)
I0903 19:41:25.471981  1419149 strace.go:567] [   2:   2] python E ioctl(0x0 /dev/pts/0, 0x5401, 0x7eb4f3d3afe0)
I0903 19:41:25.471986  1419149 strace.go:605] [   2:   2] python X ioctl(0x0 /dev/pts/0, 0x5401, 0x7eb4f3d3afe0) = 0 (0x0) (441ns)
D0903 19:41:25.471996  1419149 usertrap_amd64.go:212] [   2:   2] Found the pattern at ip 7ec8bd71c795:sysno 16
D0903 19:41:25.472001  1419149 usertrap_amd64.go:122] [   2:   2] Allocate a new trap: 0xc000359ce0 67
D0903 19:41:25.472007  1419149 usertrap_amd64.go:225] [   2:   2] Apply the binary patch addr 7ec8bd71c795 trap addr 6b4f0 ([184 16 0 0 0 15 5] -> [255 36 37 240 180 6 0])
I0903 19:41:25.472119  1419149 strace.go:567] [   5:   5] python E ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a280)
I0903 19:41:25.472128  1419149 strace.go:567] [   2:   2] python E ioctl(0x0 /dev/pts/0, 0x5401, 0x7eb4f3d3afe0)
I0903 19:41:25.472136  1419149 strace.go:605] [   5:   5] python X ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a280) = 0 (0x0) (1.964µs)
I0903 19:41:25.472141  1419149 strace.go:605] [   2:   2] python X ioctl(0x0 /dev/pts/0, 0x5401, 0x7eb4f3d3afe0) = 0 (0x0) (2.699µs)
I0903 19:41:25.472158  1419149 strace.go:567] [   2:   2] python E ioctl(0x0 /dev/pts/0, 0x5404, 0x7eb4f3d3afb0)
I0903 19:41:25.472187  1419149 compat.go:120] Unsupported syscall ioctl(0x0,0x5404,0x7eb4f3d3afb0,0x0,0x10,0x7eb4f3d3b054). It is likely that you can safely ignore this message and that this is not the cause of any error. Please, refer to https://gvisor.dev/c/linux/amd64/ioctl for more information.
I0903 19:41:25.472198  1419149 strace.go:605] [   2:   2] python X ioctl(0x0 /dev/pts/0, 0x5404, 0x7eb4f3d3afb0) = 0 (0x0) errno=25 (not a typewriter) (23.869µs)
I0903 19:41:25.472202  1419149 strace.go:567] [   5:   5] python E ioctl(0x0 /dev/pts/1, 0x5413, 0x7eb4f3d3a758)
I0903 19:41:25.472219  1419149 strace.go:605] [   5:   5] python X ioctl(0x0 /dev/pts/1, 0x5413, 0x7eb4f3d3a758) = 0 (0x0) (2.486µs)
I0903 19:41:25.472378  1419149 strace.go:567] [   5:   5] python E ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a210)
I0903 19:41:25.472386  1419149 strace.go:605] [   5:   5] python X ioctl(0x1 /dev/pts/1, 0x5401, 0x7eb4f3d3a210) = 0 (0x0) (961ns)
I0903 19:41:25.472502  1419149 strace.go:567] [   5:   5] python E write(0x1 /dev/pts/1, 0x7ec8bb5097d0 "\x1b[33mSpawning \x1b[0m\x1b[1;33m/bin/\x1b[0m\x1b[1;33mbash\x1b[0m\n", 0x32)
I0903 19:41:25.472524  1419149 strace.go:605] [   5:   5] python X write(0x1 /dev/pts/1, ..., 0x32) = 50 (0x32) (8.384µs)
I0903 19:41:25.472571  1419149 strace.go:567] [   5:   5] python E write(0x1 /dev/pts/1, 0x7ec8bb551690 "['strace', '/bin/bash']", 0x17)
I0903 19:41:25.472587  1419149 strace.go:605] [   5:   5] python X write(0x1 /dev/pts/1, ..., 0x17) = 23 (0x17) (4.354µs)
I0903 19:41:25.472574  1419149 strace.go:567] [   2:   2] python E write(0x1 host:[3], 0x7ec8bb516e50 "FAILED TO SET RAW!", 0x12)
I0903 19:41:25.472600  1419149 strace.go:605] [   2:   2] python X write(0x1 host:[3], ..., 0x12) = 18 (0x12) (5.303µs)
I0903 19:41:25.472612  1419149 strace.go:567] [   5:   5] python E write(0x1 /dev/pts/1, 0x7ec8bb717a70 "\n", 0x1)
I0903 19:41:25.472622  1419149 strace.go:605] [   5:   5] python X write(0x1 /dev/pts/1, ..., 0x1) = 1 (0x1) (2.356µs)
I0903 19:41:25.472635  1419149 strace.go:567] [   2:   2] python E write(0x1 host:[3], 0x7ec8bb717a70 "\n", 0x1)
I0903 19:41:25.472650  1419149 strace.go:605] [   2:   2] python X write(0x1 host:[3], ..., 0x1) = 1 (0x1) (6.794µs)
I0903 19:41:25.472707  1419149 strace.go:567] [   2:   2] python E write(0x1 host:[3], 0x7ec8bb513410 "(25, 'Inappropriate ioctl for device')", 0x26)
I0903 19:41:25.472721  1419149 strace.go:605] [   2:   2] python X write(0x1 host:[3], ..., 0x26) = 38 (0x26) (5.328µs)
I0903 19:41:25.472755  1419149 strace.go:567] [   2:   2] python E write(0x1 host:[3], 0x7ec8bb717a70 "\n", 0x1)
I0903 19:41:25.472767  1419149 strace.go:605] [   2:   2] python X write(0x1 host:[3], ..., 0x1) = 1 (0x1) (4.746µs)
I0903 19:41:25.472818  1419149 strace.go:567] [   2:   2] python E write(0x1 host:[3], 0x7ec8bd52b860 "0", 0x1)
D0903 19:41:25.472826  1419149 usertrap_amd64.go:212] [   5:   5] Found the pattern at ip 7ec8bd6f3c00:sysno 59
D0903 19:41:25.472842  1419149 usertrap_amd64.go:122] [   5:   5] Allocate a new trap: 0xc000d82030 68
I0903 19:41:25.472832  1419149 strace.go:605] [   2:   2] python X write(0x1 host:[3], ..., 0x1) = 1 (0x1) (4.831µs)
@thundergolfer thundergolfer added the type: bug Something isn't working label Sep 4, 2023
thundergolfer added a commit to modal-labs/modal-client that referenced this issue Sep 4, 2023
@kevinGC
Copy link
Collaborator

kevinGC commented Sep 5, 2023

TCSETSF is very similar to TCSETSW and TCSETS -- lemme see whether I can throw something together really quickly.

@kevinGC
Copy link
Collaborator

kevinGC commented Sep 5, 2023

Can you try #9339? I expect it will get things working.

@thundergolfer
Copy link
Contributor Author

Thanks @kevinGC! I'm sure that'll work, because we're currently using linux.TCSETS and the patch makes TCSETSF behave equivalently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants