Configuration Information [Automatically generated, do not change]:
Machine: aarch64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='aarch64'
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='aarch64-unknown-linux-gnu'
-DCONF_VENDOR='unknown' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash'
-DSHELL -DHAVE_CONFIG_H -I. -I../. -I.././include -I.././lib
-D_FORTIFY_SOURCE=2 -g -O2 -fstack-protector-strong -Wformat
-Werror=format-security -Wall
uname output: Linux mcbin 4.9.0+ #249 SMP PREEMPT Sun Jan 1 17:28:33 GMT 2017
aarch64 aarch64 aarch64 GNU/Linux
Machine Type: aarch64-unknown-linux-gnu
Bash Version: 4.3
Patch Level: 30
Release Status: release
Description:
Running:
while :; do s=$(sleep .005 | cat); echo $s; done | uniq
in a login shell on a virtual terminal or serial console results
in the shell randomly logging out after ^C.
Debugging reveals that:
(a) the top-level shell sets the terminal PGRP and current process
group to the shell's process ID (shell_pgrp as bash calls it).
However, when bash gets around to reading input, it receives an
EIO error - kernel debugging shows that this is because the TTY
PGRP does not match the processes PGRP at this point.
(b) the sub-shell running the "while" does not wait for the $(...)
to finish before sending itself a SIGINT.
(c) the sub-sub-shell for the $(...) waits for the "sleep" command
to terminate, and then sets the PGRP and current process group to
original_pgrp, which is the login process ID.
stracing the shell (strace -tt -ff -o str.bash -p )
and grepping the resulting log files for SPGRP shows:
str.bash.10928:17:38:38.745323 ioctl(255, TIOCGPGRP, [14119]) = 0
str.bash.10928:17:38:39.478119 ioctl(255, TIOCSPGRP, [10928]) = 0
str.bash.10928:17:38:39.480458 ioctl(255, TIOCSPGRP, [10928]) = 0
str.bash.10928:17:38:39.902027 ioctl(255, TIOCSPGRP, [10875]) = 0
str.bash.14119:17:38:38.719282 ioctl(255, TIOCSPGRP, [14119]) = 0
str.bash.14119:17:38:39.476968 ioctl(255, TIOCSPGRP, [10875]) = 0
str.bash.14120:17:38:38.718508 ioctl(255, TIOCSPGRP, [14119]) = 0
str.bash.14241:17:38:39.485459 ioctl(255, TIOCSPGRP, [10875]) = 0
str.bash.14243:17:38:39.477927 ioctl(255, TIOCSPGRP, [10875]) = 0
and stracing the login shell for terminal IO:
str.bash.10928:17:38:39.482909 write(2, "root@mcbin:~# ", 14) = 14
str.bash.10928:17:38:39.483027 read(0, "\33", 1) = 1
str.bash.10928:17:38:39.870424 read(0, 0x7fc81f8737, 1) = -1 EIO (Input/output
error)
str.bash.10928:17:38:39.884115 write(2, "\7", 1) = 1
str.bash.10928:17:38:39.884212 read(0, 0x7fc81f87a7, 1) = -1 EIO (Input/output
error)
str.bash.10928:17:38:39.899960 write(2, "logout\n", 7) = 7
From the timestamps, you can see that PID 14241 changed the TTY
PGRP between the write-out of the prompt, reading the first
character of input, and attempting to read the subsequent
characters - resulting in the shell logging itself out.
Here, pid 10875 was the login process.
Adding debug into give_terminal_to() changes the timing enough
that it becomes very difficult to trigger. Remove the debug and
it seems to be soo easy that bash is painful to use on such a
terminal.
(My command which discovered this was slightly more complex and
useful than the above, but the above is a simpler test case
which reproduces the problem.)
Repeat-By:
Login on a serial console. Probably logging in on a Linux
virtual terminal will also be good enough. Not a SSH connection,
there needs to be a login(8) process as bash's parent.
Run:
while :; do s=$(sleep .005 | cat); echo $s; done | uniq
Hit ^c. Repeat a few times. Tested on a quad-core ARM64 machine
and a dual-core 32-bit ARM machine.