Failing shell code under busybox 1.36.1 that worked with 1.31.1

David Leonard d+busybox at adaptive-enterprises.com
Thu Feb 15 08:45:44 UTC 2024


>         _result="$_result '${_p//'/'\"'\"'}'"

Busybox ash has a CONFIG_ASH_BASH_COMPAT config to get this ${var//...}
replacement behaviour.  And, in bash, the ' in the pattern section is treated
as a quote start. To give the ' a literal meaning, use \'
eg ${_p//\'/..repl..}

In current busybox ash you will run into a bug where quotes aren't
handled in the pattern, and even loses the rest of the word, as you
observed. That is, given this script:

 	var="don't g/o"
 	echo "${var/'/'o}!"
 	echo "${var/\'/\'o}!"

bash outputs:

 	don't g!			(pattern="/o" repl="")
 	don'ot g/o!			(pattern="'", repl="'o")

and busybox ash outputs:

 	don't g/o			(pattern=?, repl=?, NOTE: lost !)
 	don'ot g/o!			(pattern="'", repl="'o")

Here's a patch to make ash a little bit closer to bash.

--- a/shell/ash.c
+++ b/shell/ash.c
@@ -7058,6 +7058,7 @@ subevalvar(char *start, char *str, int strloc,
          */
         repl = NULL;
         if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
+               int quoted = 0;
                 /* Find '/' and replace with NUL */
                 repl = start;
                 /* The pattern can't be empty.
@@ -7073,6 +7074,11 @@ subevalvar(char *start, char *str, int strloc,
                                 repl = NULL;
                                 break;
                         }
+                       /* Handle quoted patterns */
+                       if ((unsigned char)*repl == CTLQUOTEMARK)
+                               quoted ^= 1;
+                       if (quoted)
+                               goto literal;
                         if (*repl == '/') {
                                 *repl = '\0';
                                 break;
@@ -7084,6 +7090,7 @@ subevalvar(char *start, char *str, int strloc,
                         /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
                         if ((unsigned char)*repl == CTLESC && repl[1])
                                 repl++;
+               literal:
                         repl++;
                 }
         }

new file mode 100644
index 000000000..23aecb5ae
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash_repl_quoted.right
@@ -0,0 +1,3 @@
+ab.
+axb.
+x/b.
new file mode 100644
index 000000000..d2ba39578
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash_repl_quoted.tests
@@ -0,0 +1,4 @@
+v=a/b
+echo "${v/'/'}."
+echo "${v/'/'/x}."
+echo "${v/'a'/x}."



On Wed, 14 Feb 2024, Harvey wrote:

> Sorry, posted the wrong version of the pack_args() funtion:
> I have payed with possible solutions for so long that I forgot the
> restore the original before posting  -blush-
>
> Here is the original version:
> ------------------------------------------------------------------------
>
> # Packs arguments into a single string.
> #
> # Input:
> #   $1 = name of variable receiving the arguments in such a way that
> #        (re)evaluating them provides the original arguments
> #   $2... = arguments
> # Example:
> #   func() {
> #     local args
> #     pack_args args "$@"
> #     send_rpc func_impl "$args"
> #   }
> #   # (in another process) > #   receive_rpc() {
> #     local func="$1"
> #     local args="$2"
> #     eval $func "$args"
> #   }
> pack_args()
> {
>     local _p _resvar=$1 _result= _value
>     shift
>     for _p
>     do
>         _result="$_result '${_p//'/'\"'\"'}'"
>     done
>     eval $_resvar=\${_result\# }
> }
> ------------------------------------------------------------------------
>
> Harvey
>
> Am 14.02.24 um 19:06 schrieb Harvey:
>> Hello,
>>
>> I am part of the team of a small router distribution called fli4l:
>> https://www.fli4l.de/doku.php?id=start.
>>
>> We use buildroot to generate our images. Due to a hardware crash and the
>> leaving of our main developer we are facing the dead of our project. But
>> there are still some people left and at least we want to try to keep it
>> alive 😉
>>
>> That said - the first we have to try is the update of our buildroot
>> base. A lot of work is already done and but we struggle with the busybox
>> update, especially with the ash shell contained. When updating to
>> Version 1.36.1 we are facing script failures in places that did work
>> until busybox 1.31.1.
>>
>> I have tried to debug the code and it seems that the error is contained
>> in a helper include for string handling.
>>
>> The code is:
>> ------------------------------------------------------------------------
>> # Packs arguments into a single string.
>> #
>> # Input:
>> #   $1 = name of variable receiving the arguments in such a way that
>> #        (re)evaluating them provides the original arguments
>> #   $2... = arguments
>> # Example:
>> #   func() {
>> #     local args
>> #     pack_args args "$@"
>> #     send_rpc func_impl "$args"
>> #   }
>> #   # (in another process)
>> #   receive_rpc() {
>> #     local func="$1"
>> #     local args="$2"
>> #     eval $func "$args"
>> #   }
>> pack_args()
>> {
>>      local _p _resvar=$1 _result= _value
>>      shift
>>      for _p
>>      do
>>          _result="$_result '${_p//'/'\"'\"'}''"
>>      done
>>      eval $_resvar=\${_result\# }
>> }
>> ------------------------------------------------------------------------
>>
>> The debug trace (using set -x) in V1.31.1 looks like this:
>>
>> + pack_args exit_trap_0 sync_unlock_all_resources
>> + local _p '_resvar=exit_trap_0' '_result=' _value
>> + shift
>> + _result=' '"'"'sync_unlock_all_resources'"'"
>> + eval 'exit_trap_0=${_result#' '}'
>> + exit_trap_0=''"'"'sync_unlock_all_resources'"'"
>> + exit_trap_num=1
>> + return 0
>>
>> ------------------------------------------------------------------------
>> while in V1.36.1 it looks like this:
>>
>> + pack_args exit_trap_0 sync_unlock_all_resources
>> + local _p '_resvar=exit_trap_0' '_result=' _value
>> + shift
>> + _result=' '"'"'sync_unlock_all_resources'
>>                                            ^^^^ missing quote
>> + eval 'exit_trap_0=${_result#' '}'
>> + exit_trap_0=''"'"'sync_unlock_all_resources'
>>                                               ^^^^ missing quote
>> + exit_trap_num=1
>> + return 0
>>
>> The eval line then fails with 'Unterminated quoted string'
>>
>> Unfortunately I can't tell where the difference lies.
>>
>> I hope someone can help or at least point me in the right direction.
>>
>> Greetings
>> Harvey
>> _______________________________________________
>> busybox mailing list
>> busybox at busybox.net
>> http://lists.busybox.net/mailman/listinfo/busybox
> _______________________________________________
> busybox mailing list
> busybox at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox
>


More information about the busybox mailing list