Subject: Re: Shell (bash, (pd)ksh, zsh, /not/ ash) + exec + here-doc + redirect == trouble!
From: [email-masked] (Eric Blake)
To: cygwin mailing-list <[email-masked]>, [email-masked]
Cc: Buzz <[email-masked]>
Date: Wed, 25 Jan 2006 20:28:32 +0000

> 
> I (now) understand what's happening. I think it's undesirable, though.

That's a relative viewpoint.  Personally, I like the fact that
the rules are consistent (process instructions one line at
a time; and apply pipelines first, then apply all redirections,
including here-docs, in left-to-right order), and that the
rules allow you to save stdin without having it be fd 0
(autoconf does this - there are programs that need to run
with stdin redirected from /dev/null, but in the rare
case that you are testing for a program that needs the same
stdin as ./configure started with, autoconf provides a macro
that can redirect the original stdin back to the program
under test).

> 
> I think the shells should redirect the input for the executed programs,
> /not/ for itself. (Could this be an exploitable vulnerability?)

In shell scripting, LOTS of things are exploitable if you write
a bad script.  Portable, safe shell is much harder to master
than portable, safe C.  But the vulnerability is not in the
shell, but in the script the shell is executing.  It boils down
to this rule of thumb: Don't install shell scripts as replacements
to compiled programs unless you trust the script is not exploitable.

> Right, my bad. Following shows the same (odd, IMO) behavior.
> 
> === begin testexec2b.sh ===
> #!/bin/bash
> 
> exec 5<&0
> echo 'echo "First exec: Done."
> exec 0<&5
> echo "Second exec: Done."
> exit 0' |exec /bin/bash
> 
> ==== end testexec2b.sh ====

Odd, but predictable.  Here, the first exec puts the original
stdin on fd 5.  Then the pipeline puts the output of
echo on the nested bash's fd 0.  Inside the nested bash,
the second exec restores the original stdin, and as
before, bash reads its next line from fd 0 at all times.

> 
> 
> I can work around this effect using -c, but thought it best to report
> anyway...

That's one of the reasons -c was invented - to allow
you to pass an entire script, not on stdin, but in argc/argv;
then you don't have to worry about where stdin is coming
from.  Shells reading from stdin should parse one line at a
time from whatever the current fd 0 is; shells reading
from -c have the entire script to execute from the getgo.

--
Eric Blake

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/