the importance of using safe interprocess communication

Jamie Heilman jamie@audible.transient.net
Thu, 7 Feb 2002 20:30:44 -0800


Executive summary: popen sucks

rdiff-backup will crash[1] on a file named \ - if that doesn't set off
all kinds of alarms in your head welcome to the 2 cent crash course in
how "sh -c" will bite you in the ass.  I haven't gone too far in
testing this to see just how much I can get away with in terms of
exploitation.  I see 3 calls to popen and one call to popen2 so this
shouldn't be too horrid to fix[2], but graceful handling of system
call errors is always a chore.  I really wish I could say this kind of
thing should be obvious but I can't because nowhere in the python docs
(that I could find) does it mention that you can avoid calls to sh -c
when using popen2 subprocesses; probably because the documentation has
been marginalized to pertain to Winblows too or something.  The one
place that you'll see immediately that popen2, popen3, and popen4 all
use the Popen3 and Popen4 classes on unix, and that when called with a
list will pass this list directly to execvp is in popen2.py from the
python distribution.  This the key to writing safe scripts which
execute external programs:

    def _run_child(self, cmd):
        if isinstance(cmd, types.StringTypes):
            cmd = ['/bin/sh', '-c', cmd]
        for i in range(3, MAXFD):
            try:
                os.close(i)
            except:
                pass
        try:
            os.execvp(cmd[0], cmd)
        finally:
            os._exit(1)

Note you can still use a string for a command when using the Popen3/4
classes, but that defeats any security.  Thats why I included mention
to the popen2 call above, I haven't actually verified that its being
passed a list and not a string.  So to the task at hand - I'll
probably start looking at a clean patch for this tonight as I've got
54G of data I'm running this on and it really sucks watching it bomb
halfway through.  What would help me tremendously in the interim is if
I could get a quicky description of the zen behind how rdiff-backup
handles errors from system calls, and what the expected mode of
failure is when a rdiff command blows chunks.

[1] crash occurs in the call to Rdiff.get_delta, reproduce with:
  mkdir foo
  touch foo/\\
  rdiff-backup foo bar
  echo > foo/\\
  rdiff-backup foo bar

[2] on lines 1095, 1108, 2052, and 3520

-- 
Jamie Heilman                   http://audible.transient.net/~jamie/
"...thats the metaphorical equivalent of flopping your wedding tackle 
 into a lion's mouth and flicking his lovespuds with a wet towel, pure 
 insanity..."						-Rimmer