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

spawn cleanup for a process with a SIGINT handler can leave stdin in echo mode #51238

Open
rtpg opened this issue Dec 20, 2023 · 0 comments
Open
Labels
child_process Issues and PRs related to the child_process subsystem.

Comments

@rtpg
Copy link

rtpg commented Dec 20, 2023

Version

v21.5.0

Platform

Linux hostname 6.5.0-14-generic #14-Ubuntu SMP PREEMPT_DYNAMIC Tue Nov 14 14:59:49 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

child_process

What steps will reproduce the bug?

  1. Place the following in a file called gnarly.js
#!/usr/bin/env node
const cp = require("child_process");
if (process.env.CHILD) {
  process.once("SIGINT", () => {});
  process.stdin.resume();
} else {
  cp.spawn("./gnarly.js", {
    stdio: [process.stdin, "pipe", process.stderr],
    env: { CHILD: 1, ...process.env },
  });
}
  1. chmod +x gnarly.js
  2. Run ./gnarly.js
  3. interrupt it with ctrl-c
  4. Try to use the up or down arrow keys in your shell after a second or so, and realize your shell is stuck in echo mode

How often does it reproduce? Is there a required condition?

consistently, every time.

What is the expected behavior? Why is that the expected behavior?

No response

What do you see instead?

My shells no longer seem to be seeing input, and instead standard input seems to be stuck in "echo mode".

zsh recovers after I SIGINT a couple more times. nushell outright gets "stuck" and no new input is visible.

Additional information

I have had trouble capturing information about this. Tools like strace and gdb seem to take stdin and do things, leading to the problem "fixing itself" when being observed directly. I also was having trouble getting gdb to follow spawned children to see what was going on there.

The main thing, though, is that I'm not exactly sure what system call or the like could be causing my problem. I think it might be related to the following ioctl call that was setting ICRNL .... but I could be totally off base (snippet of those calls). I couldn't really reliably reproduce it directly.

565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|ICRNL|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|ICRNL|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|ICRNL|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0565465 ioctl(0, TCSETSW, {c_iflag=BRKINT|ICRNL|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0

EDIT: actually I believe the following to be the specific cause, I was able to finally get strace to do its thing

The following happens during teardown (on the second SIGINT, since the first one gets absorbed by the process.once thing).

[pid 697631] ioctl(0, TCGETS,{c_iflag=BRKINT|INLCR|ICRNL|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
[pid 697631] ioctl(0, TCSETS, {c_iflag=BRKINT|ICRNL|IXON|IMAXBEL|IUTF8, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
FOR c_iflag
NO LONGER HERE:
{'INLCR'}
Added:
{'IXON'}
----------------------
FOR  c_oflag
NO LONGER HERE:
set()
Added:
set()
----------------------
FOR  c_cflag
NO LONGER HERE:
set()
Added:
set()
----------------------
FOR  c_lflag
NO LONGER HERE:
set()
Added:
{'ICANON', 'ECHO'}
----------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
child_process Issues and PRs related to the child_process subsystem.
Projects
None yet
Development

No branches or pull requests

3 participants
@rtpg @debadree25 and others