Hello,
I found a bug in bash that results in executing unfinished command when
TTY is closed.
Example: I ssh to remote machine, and start typing a command, lets say I
need to remove files that ends with .log, so I type 'rm *', and before I
can add '.log', my network drops. When that happens, 'rm *' is executed.
I investigated the bug, and I found that this happens when machine is
too slow to send SIGHUP. This also happens when I open a terminal in
GUI, and close the window (click X). To simulate this, one can just open
XTerm window, ssh to any machine (even localhost), create a process tree
long enough (just run bash in bash, depth of 6 was usually enough), type
something like 'touch /tmp/thisShouldntExists' and close the window with
terminal. Then in /tmp a file named thisShouldntExists appears. This is
just a nondestructive test case, a a destructive one is noted above.
Why this is such a problem - sometimes when logging on remote machine,
one may run few su commands, creating tree long enough for SIGHUP to be
delayed.
This bug depends strongly on machine performance, how fast will SIGHUP
get to bottom-most bash.
I understand that this is due to handling EOF from closed TTY as \n,
thus executing the command, and this is standard behavior of readline,
but I think the problem is quite serious, so I have to fix it, and I
hope that you would like this to have fixed as well.
So, my current suggested fix is attached bellow (created for Bash 4.2),
thanks for any comments to it.
Best regards,
Jiri Kukacka
--- lib/readline/readline.c 2010-07-25 14:07:40.000000000 -0700
+++ lib/readline/readline.c 2014-12-03 01:06:25.045783466 -0800
@@ -532,8 +532,17 @@
}
/* EOF typed to a non-blank line is a <NL>. */
- if (c == EOF && rl_end)
- c = NEWLINE;
+ if (c == EOF && rl_end) {
+ c = 7; /* generate BELL if stdin is force-closed */
+ /* if input is tty, readline shouldn't interpret EOF as NEWLINE
in order to prevent
+ * unwanted code execution when stdin was force-closed (i.e. ssh
dropped connection)
+ */
+ if (!isatty(fileno(stdin))) {
+ if (errno != ENXIO && errno != EIO) {
+ c = NEWLINE; /* stdin is regular file, return NEWLINE, thus
execute last line */
+ }
+ }
+ }
/* The character _rl_eof_char typed to blank line, and not as the
previous character is interpreted as EOF. */