Coprocess terminated, pipe closed before I read the data out

2016-04-12 Thread George
Configuration Information
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64'
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-pc-linux-gnu'
-DCONF_VENDOR='pc' -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 adamant 4.0.0-2-amd64 #1 SMP Debian 4.0.8-2 (2015-
07-22) x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 4.3
Patch Level: 42
Release Status: release


Description:

Coprocess's output pipe is destroyed when coprocess terminates
(possibly before coprocess output has been read)

I realize this is by design, essentially, as Bash's coprocs are meant
to be "self-cleaning" - but in practice I think this is a bad policy,
as variables and file descriptors used by the script are abruptly
removed from the environment.


Repeat-By:

$ coproc head { head -n 10; }
[1] 1864
$ ls >&${head[1]} &
# "head" gets its 10 lines, writes them to output pipe, and terminates.
$ read -r first_line <&${head[0]}
[1]- Done coproc head { head -n 10; }
$ read -r second_line <&${head[0]}
# ${head[@]} is already unset, and pipes closed, so...
-bash: ${head[0]}: ambiguous redirect


Fix:
Perhaps the simplest fix is just don't close the pipes or unset the
variable. Leave that up to the caller.

Another option might be to use poll() to check if the pipe is empty
prior to closing it:

poll_fd.events = (POLLHUP | POLLIN);
int poll_result = poll(&poll_fd, 1, 0);
// Then don't close the file descriptor until
// (!(poll_fd.revents & POLLIN) && (poll_fd.revents & POLLHUP)).

But this is still problematic: Suppose the shell script is running a
loop, processing lines of text from the coproc:

$ while read line <&${coproc[0]}; do cmd $line; done

Even if the pipe isn't closed until all the data has been read out of
it, the loop may (depending on timing) wind up terminating with an
"ambiguous redirect" error (or "unbound variable" if "nounset" is in
effect), when it should have ended happily with an EOF condition.

Users can work around this by duplicating the file descriptor:
$ exec {fd_that_wont_vanish_on_me}<&${coproc[0]}-
But it kind of negates the benefit of having coproc accept a name for
the fd array if you just wind up having to re-bind it anyway. And the
command could still fail if it's not run immediately after launching
the coproc.

Thus, I think coproc shouldn't close its file descriptors or erase its
environment variables.

---GEC





Memory leak in hc_erasedups

2016-04-12 Thread Seiichi Ishitsuka
Hi all,

I found memory leak in case of using "HISTCONTOL=erasedups" in bash-4.2.

- bash version
 CentOS7: bash-4.2.45-5.el7_0.4.x86_64

 $ echo $BASH_VERSION
 4.2.45(1)-release

- How to reproduce memory leak

 1. add ~/.bashrc

export HISTCONTROL=erasedups
export HISTSIZE=10
export PROMPT_COMMAND="history -a; history -r"

 2. login from other terminal(e.g. ssh or telnet)

 3. command execute repeatedly (e.g. echo "a"; echo "b")

  => When terminal macro is used, it's easy to reproduce.
 Following is a python script for localhost.
 Please change 'pass' to right password in your environment.

$ cat /tmp/bash-test.py
#!/usr/bin/python
import pexpect
p = pexpect.spawn ('ssh localhost')
p.expect("password:")
p.sendline('pass')
p.expect('\$ ')
while 1:
  p.sendline('echo a')
  p.expect('\$ ')
  p.sendline('echo b')
  p.expect('\$ ')
p.close()

- Result

 By executing 'bash-test.py', you can see a following anon page is increased.

$ pmap -p 26941 | head -n 5; sleep 60; pmap -p 26941 | head -n 5
26941:   -bash
0040884K r-x-- /usr/bin/bash
006dc000  4K r /usr/bin/bash
006dd000 36K rw--- /usr/bin/bash
006e6000   2468K rw---   [ anon ]

26941:   -bash
0040884K r-x-- /usr/bin/bash
006dc000  4K r /usr/bin/bash
006dd000 36K rw--- /usr/bin/bash
006e6000   2700K rw---   [ anon ]

- Proposal patch

 Attached is the testing proposal patch.

---
 bashhist.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/bashhist.c b/bashhist.c
index 7240a5b..9bf3b5c 100644
--- a/bashhist.c
+++ b/bashhist.c
@@ -625,6 +625,7 @@ hc_erasedups (line)
  char *line;
 {
   HIST_ENTRY *temp;
+  HIST_ENTRY *discard;
   int r;
 
   using_history ();
@@ -633,7 +634,9 @@ hc_erasedups (line)
   if (STREQ (temp->line, line))
{
  r = where_history ();
- remove_history (r);
+ discard = remove_history (r);
+ if (discard)
+   free_history_entry (discard); 
}
 }
   using_history ();
-- 
1.8.3.1

 I have tested at bash-4.2 branch.
 url = http://git.savannah.gnu.org/r/bash.git

Best regards,
Seiichi Ishitsuka




bash "while do echo" can't function correctly

2016-04-12 Thread 何建军
Hi, I use a very simple statement "cat test.txt | while read line; do echo 
$line; done",  test.txt is very small, no more than 100 lines. but the 
execution of the statement  paused during process. test.txt is pasted on 
http://paste.bradleygill.com/index.php?paste_id=1647399 
desmond.he@xgimi-dev:/studio/desmond.he$ help | head -n 1 GNU bash, version 
4.3.11(1)-release (x86_64-pc-linux-gnu) 
desmond.he@xgimi-dev:/studio/desmond.he$ cat /etc/lsb-release DISTRIB_ID=Ubuntu 
DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 
14.04.3 LTS" Thanks for help~

Re: bash "while do echo" can't function correctly

2016-04-12 Thread Geir Hauge
On Wed, Apr 13, 2016 at 10:01:23AM +0800, 何建军 wrote:
> Hi, I use a very simple statement "cat test.txt | while read line; do echo 
> $line; done",  test.txt is very small, no more than 100 lines. but the 
> execution of the statement  paused during process. test.txt is pasted on 
> http://paste.bradleygill.com/index.php?paste_id=1647399 
> desmond.he@xgimi-dev:/studio/desmond.he$ help | head -n 1 GNU bash, version 
> 4.3.11(1)-release (x86_64-pc-linux-gnu) 
> desmond.he@xgimi-dev:/studio/desmond.he$ cat /etc/lsb-release 
> DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty 
> DISTRIB_DESCRIPTION="Ubuntu 14.04.3 LTS" Thanks for help~

You didn't quote the expansion of the line variable, so the result of
the expansion is first split into words based on the characters in the
special IFS variable, then each of those words that contain glob
characters (like *, ? , [...]) will be replaced by matching filenames,
if any.

It's the last part that bites you here, since several of your lines
contain * characters. Worse if globstar is enabled, since there's also
** in there, so the "pause" you get is probably bash trying to recurse
through the entire filesystem, which may take a while.

The fix is easy. Just surround the expansion in double quotes, which
prevent word-splitting and pathname expansion.

while read -r line; do echo "$line"; done < test.txt

though printf should be preferred over echo:

while read -r line; do printf '%s\n' "$line"; done < test.txt

-- 
Geir Hauge