Hello, 

Recently we came across a bug in bash which was introduced by below patch : 

http://git.savannah.gnu.org/cgit/bash.git/commit/?id=d5d00961 

In bash 4.2 this could lead to a race condition. 'yy_readline_get ()' function 
sets 'terminate_immediately' to 1 before calling readline() at [1]. 

If shell receives SIGHUP and executes 'termsig_handler ()' before setting 
'terminate_immediately' back to 0 [2], we will end up at [3] (considering 
'RL_ISSTATE (RL_STATE_READCMD)' evaluates to 0 when readline is not waiting to 
read command from user), and ~/.bash_history will not be updated. 

We started seeing this bug after above mentioned patch was backported to RHEL 
6. Sometimes ~/.bash_history is not updated when user executes 'reboot' command 
in a ssh session. This is caused by race condition in handling SIGHUP. 

While this issue was fixed by backporting somes changes (See attached patch) 
from [4] to bash-4.2 or older versions, there is still a corner case which may 
cause race condition in handling SIGHUP in current upstream. 

'bash_tilde_expand ()' function sets 'terminate_immediately' to 1 at [5] 

If SIGHUP is received and termsig_handler () gets executed before reaching [6], 
~/.bash_history will not be updated. This can happen in a scenario where user 
is running ssh session and requests for expansion for '~', if an admin issues 
'reboot' command at the same time then ~/.bash_history may not be updated. 

I have 2 questions about how current upstream handles this condition : 

1. Why 'terminate_immediately' is set to 1 at [7]? 

2. Why 'RL_ISSTATE (RL_STATE_READCMD)' is being checked at [8]? This will 
evaluate to 0 if readline is not waiting to read a command from user. I believe 
this check can be removed. 

-------------------- 
[1] http://git.savannah.gnu.org/cgit/bash.git/tree/parse.y?id=bash-4.2#n1441 
[2] http://git.savannah.gnu.org/cgit/bash.git/tree/parse.y?id=bash-4.2#n1446 
[3] 
http://git.savannah.gnu.org/cgit/bash.git/tree/sig.c?id=d5d0096115d0d484fd669ad170498962ea45e841#n513
 
[4] 
http://git.savannah.gnu.org/cgit/bash.git/commit/?id=ac50fbac377e32b98d2de396f016ea81e8ee9961
 
[5] http://git.savannah.gnu.org/cgit/bash.git/tree/general.c#n994 
[6] http://git.savannah.gnu.org/cgit/bash.git/tree/general.c#n1004 
[7] http://git.savannah.gnu.org/cgit/bash.git/tree/general.c#n994 
[8] http://git.savannah.gnu.org/cgit/bash.git/tree/sig.c#n524 

-- 
Siteshwar Vashisht 
Software Engineer 
From 1ecc0ff050d74aef548677fb97b2113b0920c711 Mon Sep 17 00:00:00 2001
From: Siteshwar Vashisht <svashi...@redhat.com>
Date: Mon, 25 Apr 2016 14:04:10 +0530
Subject: [PATCH] Do not set terminate_immediately while reading input

---
 builtins/read.def | 3 +--
 parse.y           | 5 +----
 y.tab.c           | 5 +----
 3 files changed, 3 insertions(+), 10 deletions(-)

diff --git a/builtins/read.def b/builtins/read.def
index 1ef9142..2dcaf35 100644
--- a/builtins/read.def
+++ b/builtins/read.def
@@ -455,7 +455,6 @@ read_builtin (list)
      of the unwind-protect stack after the realloc() works right. */
   add_unwind_protect (xfree, input_string);
   interrupt_immediately++;
-  terminate_immediately++;
 
   unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
 
@@ -512,6 +511,7 @@ read_builtin (list)
 
       if (retval <= 0)
 	{
+	  CHECK_TERMSIG;
 	  eof = 1;
 	  break;
 	}
@@ -616,7 +616,6 @@ add_char:
     zsyncfd (fd);
 
   interrupt_immediately--;
-  terminate_immediately--;
   discard_unwind_frame ("read_builtin");
 
   retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
diff --git a/parse.y b/parse.y
index 9a78d0c..0b9f9ff 100644
--- a/parse.y
+++ b/parse.y
@@ -1440,12 +1440,11 @@ yy_readline_get ()
 	  old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler);
 	  interrupt_immediately++;
 	}
-      terminate_immediately = 1;
 
       current_readline_line = readline (current_readline_prompt ?
       					  current_readline_prompt : "");
 
-      terminate_immediately = 0;
+      CHECK_TERMSIG;
       if (signal_is_ignored (SIGINT) == 0 && old_sigint)
 	{
 	  interrupt_immediately--;
@@ -1606,13 +1605,11 @@ yy_stream_get ()
       if (interactive)
 	{
 	  interrupt_immediately++;
-	  terminate_immediately++;
 	}
       result = getc_with_restart (bash_input.location.file);
       if (interactive)
 	{
 	  interrupt_immediately--;
-	  terminate_immediately--;
 	}
     }
   return (result);
diff --git a/y.tab.c b/y.tab.c
index d702554..563d312 100644
--- a/y.tab.c
+++ b/y.tab.c
@@ -3757,12 +3757,11 @@ yy_readline_get ()
 	  old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler);
 	  interrupt_immediately++;
 	}
-      terminate_immediately = 1;
 
       current_readline_line = readline (current_readline_prompt ?
       					  current_readline_prompt : "");
 
-      terminate_immediately = 0;
+      CHECK_TERMSIG;
       if (signal_is_ignored (SIGINT) == 0 && old_sigint)
 	{
 	  interrupt_immediately--;
@@ -3923,13 +3922,11 @@ yy_stream_get ()
       if (interactive)
 	{
 	  interrupt_immediately++;
-	  terminate_immediately++;
 	}
       result = getc_with_restart (bash_input.location.file);
       if (interactive)
 	{
 	  interrupt_immediately--;
-	  terminate_immediately--;
 	}
     }
   return (result);
-- 
2.5.5

Reply via email to