Failing shell code under busybox 1.36.1 that worked with 1.31.1

Harvey harv at gmx.de
Fri Feb 16 12:28:56 UTC 2024


BTW, do we have to propose this patch somewhere else or is this already 
the right place?

Greetings
Harvey

Am 15.02.24 um 22:48 schrieb Harvey:
> David,
> 
> I can confirm that your patch solves my problem without any further need 
> to change the shell code. Now V 1.36.1 acts in the same way as V 1.31.1 
> did. Thank you so mutch! Maybe this is candidate to merge ;)
> 
> Greetings
> Harvey
> 
> Am 15.02.24 um 09:45 schrieb David Leonard:
>>
>>>         _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
>>>
> _______________________________________________
> busybox mailing list
> busybox at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox


More information about the busybox mailing list