6

I am updating bash on our embedded platform from 4.1.9 to the latest (4.4.12), and I am seeing a behaviour change in this simple scenario of passing escaped arguments into a script.

Script /tmp/printarg:

#! /bin/sh
echo "ARG |$*|"

And I invoke the script like this:

bash -c "/tmp/printarg \\"abc\\""

I've tried this on several platforms (native x86_64 Linux) running bash 4.3.42, as well as several embedded platforms (ARM and PPC) running bash 4.1.9 and 4.2.37, and all of these platforms report what I would expect:

38$ bash -c "/tmp/printarg \\"abc\\""
ARG |abc|

But, when I run this using bash 4.4.12 (native X86 or embedded platforms), I get this:

$ bash -c "/tmp/printarg \\"abc\\""
ARG |abc\|            <<< trailing backslash

And if I add a space in the command line between the second escaped quote and the ending quote, then I no longer see the extra backslash:

$ bash -c "/tmp/printarg \\"abc\\" "
ARG |abc |            <<< trailing space, but backslash is gone

This feels like a regression. Any thoughts? I also did try enabling the various compat options (compat40, compat41, compat42, compat43) with change.

  • 1
    I don't think "backquote" means what you think it means. – DopeGhoti Jan 17 '18 at 19:28
  • D'oh, sorry! Make that "backslash". – Steve Hein Jan 17 '18 at 19:34
  • Can you show the contents of /tmp/quotefail too? Also, result of ls -l /bin/sh may be of interest. – Gerard H. Pille Jan 17 '18 at 19:46
  • I get ARG |abc\| on Debian testing (buster) amd64 with bash 4.4.12(1)-release and /bin/sh set to dash (but that doesn't seem to matter; replacing printargs with a Perl program confirms) – derobert Jan 17 '18 at 19:55
  • BTW, if you mean your script to be run with bash, then a /bin/sh shebang is very poor form. – Charles Duffy Jan 17 '18 at 22:52
9
bash -c "/tmp/printargs \\"abc\\""

Does not escape what you think it does. A backslash-backslash is an escaped backslash, handled by the calling shell — so that is the same as running:

/tmp/printargs \abc\

because the double-quotes are not escaped. You could have just written:

bash -c '/tmp/printargs \abc\'

I'm guessing you actually wanted:

bash -c "/tmp/printargs \"abc\""

which escapes the double quotes, passing a quoted "abc" to the bash -c.

(I'm guessing the different behavior you're seeing is different versions of bash handling the escaped nothing at end of input differently.)

Perl version of printargs (slightly improved behavior):

#!/usr/bin/perl
use feature qw(say);

for (my $i = 0; $i < @ARGV; ++$i) {
        say "$i: |$ARGV[$i]|";
}
  • 1
    Yes, this is what I actually wanted! I was incorrectly thinking that the extra backslash escaped the quote through an additional level of shell. When I changed my original code to use a single backslash, things worked as expected in both older and newer bash bash versions. (Seems that the additional backslash was benign in older bash versions). Thanks @derobert! – Steve Hein Jan 17 '18 at 20:36
  • @SteveHein It wasn't actually quoted before — which maybe isn't an issue, or might be a major one. Depending on what you're doing (and in particular where that "abc" parameter comes from & how it is validated) that could be a security vulnerability allowing remote code execution. (And do note that just putting it in quotes may not be sufficient, depending on how "abc" is validated). – derobert Jan 17 '18 at 21:03
4
bash -c "/tmp/printargs \\"abc\\""

Are you sure this is what you want to do? If you run that with set -x in effect, you'll see that the command that runs, is

bash -c '/tmp/printargs \abc\'

i.e. you're passing the shell a string that ends in a backslash. Your first quoted string contains an escaped backslash, then you have an unquoted abc, an escaped backslash, and then an empty quoted string. (Note how the syntax highlighting done by Stackexchange shows the abc is not quoted.)

The unquoted backslash at the end of input doesn't make much sense. A backslash either escapes the following character, or starts a continuation line, where it's deleted along with the following newline, like here:

$ bash -c $'echo "foo\\\nbar"'                                                                                                              
foobar 

This case has neither. You're possibly trying to do either of these:

bash -c "/tmp/printargs \"abc\""
bash -c '/tmp/printargs "abc"'

Both of which produce the output ARG |abc|.


We can see the difference between shells with a bit simpler test:

$ bash -c 'echo $BASH_VERSION; echo abc\'
4.4.12(1)-release
abc\

$ ./bash -c 'echo $BASH_VERSION; echo abc\'
4.3.30(1)-release
abc

$ dpkg -l dash |grep ^i
ii  dash           0.5.8-2.4    amd64        POSIX-compliant shell
$ dash -c 'echo abc\'
abc\

$ dpkg -l zsh |grep ^i
ii  zsh            5.3.1-4+b2   amd64        shell with lots of features
$ zsh -c 'echo abc\'
abc

If I had to guess, I'd start looking for the source of the change in this change:

This document details the changes between this version, bash-4.4-alpha, and
the previous version, bash-4.3-release.

1.  Changes to Bash

cccc. Fixed a bug that resulted in short-circuited evaluation when reading
      commands from a string ending in an unquoted backslash, or when sourcing
      a file that ends with an unquoted backslash.

  • Thanks. There are clearly better ways to do this. I created this example to identify a problem that I was having in a more complex set of scripts where an arg is passed through multiple subscripts. And there is probably a better way to rework the original script, but as I said, what I have here works without issue on a number of older bash versions. My main exercise here is to move an existing platform to a new bash version (to eliminate ShellShock vulnerability). So any incompatibilities that I see are a concern. – Steve Hein Jan 17 '18 at 20:16
  • Are folks here saying that what I have should have never worked in the first place? – Steve Hein Jan 17 '18 at 20:18
  • @SteveHein, if what you're doing is like the snippet in your question, you might want to check your quoting. And yes, I do think that the ending backslash makes no sense. – ilkkachu Jan 17 '18 at 20:21
  • 1
    @ilkkachu Here is more info: changelog. – PesaThe Jan 17 '18 at 20:40
  • @PesaThe, right, makes sense if it's a fix to an actual bug. Though I'm not sure a fix that changes visible behaviour in other cases was the best one, but it's a corner case anyway. – ilkkachu Jan 17 '18 at 20:55
1

I'll explain this one:

$ bash -c "/tmp/quotefail \\"abc\\" "
ARG |abc |            <<< trailing space, but backquote is gone

As @ilkkachu explained, with set -x we see how this is interpreted:

+ bash -c '/tmp/quotefail \abc\ '

Of course, "\a" is just "a", and "\ " is just " ", so the parameter /tmp/quotefail receives is "abc ", and the result:

ARG |abc |

In the first test, the backslash wasn't followed by anything, so it remained a backslash.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.