http://qs321.pair.com?node_id=690757

Apologies for the somewhat inflammatory title, but I've given the issue a fair amount of thought, and I've come to the following conclusion: perl should do more to discourage the use of system() with a single, non-constant argument.

At the very least it should emit a warning, and ideally it should it should refuse to compile since this can be detected at compile time. The way it works now gives people too many ways to shoot themselves in the foot from both a correctness perspective as well as a security perspective.


Update: merlyn has provided an answer to the following issue in his reply.

In fact, the system() routine has a flaw: there is no way to invoke a command with no arguments without having it scanned for shell metacharacters and spaces. That is, in the following example:

my $binary = "/bin/foo bar"; system($binary);
system will always try to execute /bin/foo with argument bar. But what if my executable is /bin/foo bar?


Another partial remedy would be having certain modules (like CGI) disable the use of a single-argument call to system. Users would be forced to call CORE::system() or something.

Of course, that raises the question of what to replace something like system("foo $arg | grep whatever") with. It'll take a little work, but I'm sure we can come up with a convenient API for duplicating what a shell does when setting up a pipeline of commands and redirecting IO. And one can always invoke the shell directly with system("/bin/sh", "-c", ...) if you like to live dangerously.

Ok, I'll get off my soapbox now.

Replies are listed 'Best First'.
Re: Use of system() considered harmful
by merlyn (Sage) on Jun 06, 2008 at 20:52 UTC
    my $binary = "/bin/foo bar"; system($binary);
    system will always try to execute /bin/foo with argument bar. But what if my executable is /bin/foo bar?
    Then you use:
    system { $binary } $binary, @other_args_if_any;
    That forces "list" interpretation rather than single-element list, and all is good.
Re: Use of system() considered harmful
by Erez (Priest) on Jun 07, 2008 at 09:32 UTC

    A few points.

    perl should do more to discourage the use of

    Perl doesn't, by design, discourage anything. This is why strict and warnings are not enabled by default, among else. Out-of-the-box Perl allows you to do many things that you shouldn't. It is your choice as a programmer of whether to use those or not. There are pragmas and modules (strict, warnings, taint mode, Perl::Critic) that can help you enforce those choices. But the language itself isn't one of them.

    And while on the subject, "Considered Harmful" Essays are also Considered Harmful.

    Stop saying 'script'. Stop saying 'line-noise'.
    We have nothing to lose but our metaphors.

Re: Use of system() considered harmful
by moritz (Cardinal) on Jun 06, 2008 at 21:03 UTC
    I just create a shell script in /bin/sleep 4, and this:
    system '/bin/sleep 4', '';

    calls the shell script instead of /bin/sleep with argument 4.

    I don't know how many programs take offense from a single, empty argument.

Re: Use of system() considered harmful
by kyle (Abbot) on Jun 06, 2008 at 21:01 UTC
    But what if my executable is /bin/foo bar?

    Is this what you mean?

    my $sh = '/home/kyle/harm/spacey name.sh'; open my $sh_fh, '>', $sh or die "Can't write '$sh': $!"; print $sh_fh 'echo "spaces are fun!"'; close $sh_fh; chmod 0755, $sh or die "Can't chmod '$sh': $!"; $sh =~ s/ /\\ /g; print qq{I shall now: system( "$sh" );\n}; system( "$sh" ); __END__ I shall now: system( "/home/kyle/harm/spacey\ name.sh" ); spaces are fun!

    Or are you talking about something more ambiguous?

Re: Use of system() considered harmful
by sgifford (Prior) on Jun 06, 2008 at 22:43 UTC
    It's also worth noting that using taint mode and a little care will help prevent insecure arguments from doing nasty things to the shell when running system.

    Update: Clarified the types of security bugs taint mode helps with.

Re: Use of system() considered harmful
by zentara (Archbishop) on Jun 07, 2008 at 15:51 UTC
    Anything that forks off, or executes an external program can be unpredictable...... but I would rather have it easy to use and test it myself before use, than have Perl prohibit me from doing it, like in cgi taint mode.

    The testing for correctness extends further too, when you pass in an @args, like system('/bin/foo',@args), how can you be sure how foo is parsing @args? I've seen weird input processing where you need to explicitly specify input pairs, like system('/bin/foo','-p bar','-z wham', '-x ','one two three')

    So in other words, Perl makes it easy for 99% of cases, but you need to be careful, and it's up to the programmer to watch for errors, with something along the lines

    system("cmdtorun,@args >error.txt 2>&1");

    I have this snippet saved from ChemBoy which may be useful

    #!/usr/bin/perl #by ChemBoy of perlmonks # Ever gotten annoyed at how different from every other subroutine # call system is? Ever screwed up your error reporting because you # couldn't remember what to do with $?, $@ and $!? I sure have... # and I got sick of rewriting this over and over, so I wrapped it # in a utility subroutine, and here it is. It assumes the # system LIST style is being used, because system SCALAR is # somewhat hazardous and (IMO) to be discouraged, but obviously # it's readily modified to be more tolerant. # Usage: # wrap_system(qw(perl -u -e 1)) or die "$@\n" # perl exited with status 0 after receiving signal 6 (core dumped) wrap_system(qw(perl -u -e 1)) or die "$@\n"; sub wrap_system { if ( 0 == system { $_[0] } @_ ) { return 1 } if ( -1 == $? ) { $@ = "Unable to launch $_[0]: $!" } else { $@ = "$_[0] exited with status " . ($? >> 8); if (my $sig = $? & 127) { $@ .= " after receiving signal $sig" +} if ($? & 128) {$@ .= " (core dumped)" } } return; }

    I'm not really a human, but I play one on earth CandyGram for Mongo
Re: Use of system() considered harmful
by BrowserUk (Patriarch) on Jun 06, 2008 at 21:11 UTC
      It's dangerous because shell interpretation can change the behavior of your program in surprising ways. Command line programs work with positional parameters, and when you subject your command text to shell interpretation you have to very careful in the way you write the command to preserve those positional parameters.

      For instance, this looks like it will invoke cmd with three arguments:

      system("/bin/sh", "-c", "cmd $one $two $three");
      Of course, what really will happen depends on what the strings $one, $two and $three contain.

      That's why I like to avoid invoking a shell either explicitly or implicitly when calling external programs.

        That's why I like to avoid invoking a shell either explicitly or implicitly when calling external programs.

        Isn't that exactly why system has the list argument form?

        Sounds to me like you are asking for perl to restrict everyones access to useful behaviour in order to save you from yourself.

        Of course, what really will happen depends on what the strings $one, $two and $three contain.

        No shit Sherlock :) And the result of print $a + $b + $c; will depend upon what's in $a, $b & $c.

        I once wrote that I considered ...considered harmful., harmful. And nothing I've seen in the intervening 6 years has done anything to change my mind. Most times when someone writes "XXX considered harmful", they are really saying "I just got bitten by XXX" and so I think that Perl/government/other should do something to save me from myself. And this is no exception.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Use of system() considered harmful
by pc88mxer (Vicar) on Jun 07, 2008 at 15:41 UTC
    merlyn, thanks for pointing out how to force list interpretation for system.

    I think my /bin/foo bar example was a red herring. A better example of what I would like to see perl discourage is the the following use of system to execute a single command:

    system("cmd $with $some $args")
    Because the argument is subject to shell interpretation, this code exposes itself to just too many pitfalls. If we saw this in someone else's code we'd clearly point out that it's unsafe.

    If you really want to subject your string to shell interpretation, why not just explicitly call the shell?

    It's good that perl provides a way to force list context with system. However, I'll argue that it isn't getting used when it needs to. Users are introduced to this:

    system("/bin/ls"); # run /bin/ls with no arguments
    and they generalize to:
    system($cmd); # run the $cmd command with no arguments
    not:
    system {$cmd}, $cmd;
    Even when we tell them to use the list version of system to safely preserve passed arguments, they can be in for a surprise. They see this:
    system($cmd, $with, $some, $args);
    and generalize to:
    system($cmd, @args);
    and then wonder why it doesn't work as expected when @args is the empty list.
      system {$cmd}, $cmd;

      What do the curlies do there? I don't see anything like that syntax in the manpage for system.

        This is the "system PROGRAM LIST" syntax. It's documented more fully under exec. There are some cross-references in the system page, but I can see how you could easily miss them if you weren't looking out for something like that. Maybe the documentation could usefully be expanded to add an example of this syntax.

        (pc88mxer's version actually contains a syntax error, BTW. There shouldn't be a comma after the curlies -- it's an indirect argument, like the filehandle in print.)

Re: Use of system() considered harmful
by sundialsvc4 (Abbot) on Jun 11, 2008 at 13:23 UTC

    Strictly speaking, you can use grep -rilw system * in the source-directory to quickly locate all files which contain the bareword-delimited string "system."

    To me, this is "a necessary design-decision" that the programmer must make. The ultimate goal of any program is both "to solve the problem" and "to be maintainable." Usually, I advocate putting the environment-specific stuff (like system() calls) into a separate module or package, so that everything's in one-and-only-one place. But if the system() approach appears to me to be a reasonable way to solve the problem, I'm gonna do it ... and document the hell out of it.

    What I emphatically don't want is a bunch of widely-scattered and not-obvious code that is both poorly documented and causally-connected. I've fixed a'plenty of code like that... cursing the name of the original programmer (and the legitimacy of his birth-parent's relationship) all the while. ;-)

Re: Use of system() considered harmful
by mr_mischief (Monsignor) on Jun 23, 2008 at 17:20 UTC
    You do realize that to many people the phrase "X considered harmful" is, well, considered harmful? It's often used with the tongue and a cheek being very friendly to one another.

    What's usually meant by such statements is that without due care, a fairly mundane and common practice can have unpleasant consequences. The prototypical case is "goto() considered harmful", which of course is silly at some level because most structured code simply abstracts it and does nothing to remove its proper use. The Djikstra article was much more conservative in its complaints about goto() than most who know it only by reference would guess. The author says that structured program flow was easier to follow, debug and formally prove. He does not say that careful use of goto() in some instances is bad, only that it was typical at the time to overuse and abuse the feature. Only improper use is the problem, as it is with system(). In fact, almost anything in our lives could be put to the same claim. It's not a perfect world we live in. Nothing's without risk, but we go on living. Overstating danger to get noticed is for news reporters and politicians, not programmers.

    From the top of my head, "crosswalks considered harmful -- every intersection should have a bridge or tunnel", "kitchen knives considered harmful -- everyone should only eat things easily bitten apart, because you might cut a finger or hand", "firearms considered harmful -- if misused or handled recklessly, they can be deadly", "sex considered harmful -- if you're not careful you could catch something", and "tomatoes and spinach considered harmful -- if you raise, harvest, or handle most fruits and vegetables improperly, they might carry bacteria like salmonella or e. coli".

    This last one has been a big deal recently in the US, actually, not because tomatoes are dangerous (and not even that salmonella is usually particularly dangerous where it's expected and properly mitigated). It's the improper handling that's the problem.Animals that carry salmonella in their meat should always be fully cooked, and animals that carry it in the intestines should be slaughtered and butchered responsibly (and their meat still cooked thoroughly on the surface if unsure). Tomatoes, not having intestines, should probably not be expected to carry an intestinal bacterial culture. With proper care, they don't.

    Such is the issue with system(). There are risks, but most of them have to do with improper use and handling. Most of them can be either eliminated or mitigated with proper care. If its usefulness outweighs the risks, then use it. The risks rise more in certain situations, so more mitigation is needed. Perhaps in some cases there's too much risk, but in most of those calling any outside program should be strongly questioned in the first place.