bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bash leaks the old var when using =~ in a function with local BASH_REMAT


From: Emanuele Torre
Subject: bash leaks the old var when using =~ in a function with local BASH_REMATCH
Date: Sat, 28 May 2022 22:08:19 +0200

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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]