Summary
On a wasm32 machine (glibc distro dmitry/linux-6.12-glibc-bash-coreutils, browser runtime), tty job control is broken in two ways that combine to make every forked process input-blind on the controlling terminal:
tcsetpgrp()is inert. The tty's foreground pgrp never changes —tpgidstays1(pid 1's group) no matter who callstcsetpgrp()on the controlling tty. Verified from/proc/*/statfield 8 across multiple runs.fork()places the child in a fresh process group equal to its pid, even when userspace makes nosetpgid()call at all (bash with job control off,set +m). The child also inherits itscommfrom the kthread donor task — it shows up as(swapper/0)— so the__wasm32_alloc_process_taskrecycling path appears to initialize task fields from the donor instead of the parent.
Combined effect: child pgrp ≠ tty foreground pgrp (which is pinned at 1 forever), so any forked child that reads from the controlling tty fails the tty job-control check and gets -EIO. Only pid 1 itself (or an exec from pid 1, i.e. no fork) can read the terminal.
Reproduction (in-browser terminal of the machine above)
a2$ echo flags=$-
flags=hiBHs # job control OFF — bash makes no setpgid calls
a2$ cat -v
cat: -: Input/output error
a2$ cat /proc/1/stat
1 (init) D 0 1 1 34816 1 256 ... # pgrp=1 session=1 tty=pts/0 tpgid=1
a2$ bash -c "cat /proc/self/stat"
35 (swapper/0) R 1 35 1 34816 1 0 ... # pid=35 pgrp=35 (!) session=1 tpgid=1
The forked child (pid 35) sits in pgrp 35 while the tty's foreground pgrp is 1 → its tty reads return EIO. Note comm=(swapper/0) on a plain fork of bash.
Kernel log line seen at each fork: __wasm32_alloc_process_task(pid_sentinel=0, ppid=1) -> ... pid=35.
Impact
Interactive full-screen apps (mc, editors) and anything reading stdin (cat, dd) are dead when launched normally from the shell. Midnight Commander additionally hangs at startup because its subshell-helper fork never comes up.
Workarounds in use
/.bashrcwithset +m(helps on consoles where the pgrp survives fork — our fra2 host harness; NOT sufficient in the browser, where fork itself reassigns the pgrp).- Run tty apps without forking:
TERM=ansi exec /usr/bin/mc -u(mc replaces pid 1;-udisables the subshell fork). Verified working in the browser.
Expected
fork()must leave the child in the parent's process group (POSIX), andcommmust be inherited from the parent.tcsetpgrp()on the controlling tty must update the foreground pgrp so shells can hand the terminal to job pgrps.