Configuration Information [Automatically generated, do not change]: Machine: x86_64 OS: linux-gnu Compiler: gcc Compilation CFLAGS: -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -DDEFAULT_PATH_VALUE='/usr/local/sbin:/usr/local/bin:/usr/bin' -DSTANDARD_UTILS_PATH='/usr/bin' -DSYS_BASHRC='/etc/bash.bashrc' -DSYS_BASH_LOGOUT='/etc/bash.bash_logout' -DNON_INTERACTIVE_LOGIN_SHELLS uname output: Linux t420 5.15.41-1-lts #1 SMP Wed, 18 May 2022 13:37:06 +0000 x86_64 GNU/Linux Machine Type: x86_64-pc-linux-gnu
Bash Version: 5.1 Patch Level: 16 Release Status: release Description: If `[[ $str =~ $re ]]' is executed from a function in which `BASH_REMATCH' is local, bash will "leak" the old *global* `BASH_REMATCH' variable. This happens because in `sh_regmatch()', bash calls these two functions: unbind_variable_noref ("BASH_REMATCH"); rematch = make_new_array_variable ("BASH_REMATCH"); `unbind_variable_noref()' will unbind and `free()' the first variable it can find named "BASH_REMATCH" (giving priority to local variables). While "BASH_REMATCH" will add a new variable named "BASH_REMATCH" to the global variables. Since the old BASH_REMATCH variable was not removed, the old variable will not be readable until the new one is removed (using `unset -v BASH_REMATCH'). Repeat-By: bash-5.1$ x=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bash-5.1$ a () [[ $x =~ .* ]] bash-5.1$ (ulimit -v 5000; for ((i=0;i<8000;++i)); do a; done) bash-5.1$ a () { local BASH_REMATCH; [[ $x =~ .* ]] ;} bash-5.1$ (ulimit -v 5000; for ((i=0;i<8000;++i)); do a; done) bash: xmalloc: cannot allocate 32 bytes bash-5.1$ a () { local BASH_REMATCH; [[ $1 =~ .* ]] ;} bash-5.1$ declare -p BASH_REMATCH bash: declare: BASH_REMATCH: not found bash-5.1$ a abc; declare -p BASH_REMATCH declare -a BASH_REMATCH=([0]="abc") bash-5.1$ a xyz; declare -p BASH_REMATCH declare -a BASH_REMATCH=([0]="xyz") bash-5.1$ a hello; declare -p BASH_REMATCH declare -a BASH_REMATCH=([0]="hello") bash-5.1$ unset -v BASH_REMATCH; declare -p BASH_REMATCH declare -a BASH_REMATCH=([0]="xyz") bash-5.1$ unset -v BASH_REMATCH; declare -p BASH_REMATCH declare -a BASH_REMATCH=([0]="abc") bash-5.1$ unset -v BASH_REMATCH; declare -p BASH_REMATCH bash: declare: BASH_REMATCH: not found This also occurs on the devel branch. Fix: The obvious fix is to use, instead of `unbind_variable_noref()', a similar function that uses `global_variables' instead of `shell_variables'. That will remove the "variable leak", but it is still not great: declaring a local `BASH_REMATCH' makes it impossible to access the matches of `[[ $str =~ $re ]]' because bash will set the global `BASH_REMATCH' instead of the local one, and `"${BASH_REMATCH[@]}"' will expand to local `BASH_REMATCH'. I think allowing `BASH_REMATCH' to be local-ised should be considered: it would be nice. (Also, it's a little confusing that `MAPFILE', `REPLY', `COPROC', etc. can be localised, but `BASH_REMATCH' cannot.) bash will currently (once the unbind part is fixed) try to remove the global `BASH_REMATCH' and replace it with a brand new array variable that contains the matches. It could instead replace the local `BASH_REMATCH' variable with a new local array variable (if a local `BASH_REMATCH' variable of any type was present.) I am not sure if bash has any specific reason to use this technique instead of just using `find_or_make_array_variable()' like other features in bash that use arrays do. I think bash could just make `[[ $str =~ $re ]]' use `find_or_make_array_variable()' like other bash features that use arrays do; If the variable that already exists has incompatible attributes (i.e. -A and -r) it could just print an error message (while still returning 0/1 depending on the result of the match, BASH_REMATCH not being settable should not influence the exit status of `[[ $str =~ $re ]]'), or simply not set BASH_REMATCH silently. This would also allow to use attributes like -l, -u with BASH_REMATCH (`declare -l BASH_REMATCH') which may be useful.