URL:
  <https://savannah.gnu.org/bugs/?67473>

                 Summary: ":|eval (exit 42)" does not return 42
                   Group: The GNU Bourne-Again SHell
               Submitter: pmhahn
               Submitted: Di 02 Sep 2025 16:38:44 GMT
                Category: None
                Severity: 3 - Normal
              Item Group: None
                  Status: None
                 Privacy: Public
             Assigned to: None
             Open/Closed: Open
         Discussion Lock: Any


    _______________________________________________________

Follow-up Comments:


-------------------------------------------------------
Date: Di 02 Sep 2025 16:38:44 GMT    By: Philipp Hahn <pmhahn>
The GitLab runner showed a strange behavior, where the exit code of a shell
command did not show up correctly and was always 1. GitLab fixed that by
changing their code how they call `bash`. Details at
https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27668


$ bash -e -c ': | bash -c "exit 42"'; echo $?
42
$ bash -e -c ': | eval "exit 42"'; echo $?
42
$ bash -e -c '    eval "bash -c \"exit 42\""'; echo $?
42
$ bash -e -c '    eval "(exit 42)" </dev/null'; echo $?
42
$ bash -e -c ': |(eval "(exit 42)" )'; echo $?
42
$ bash -e -c 'shopt -s lastpipe;: | eval "(exit 42)"';echo $?
42
$ bash -e -c ': | eval "bash -c \"exit 42\""'; echo $?
1
$ bash -e -c ': | eval "eval \"bash -c \\\"exit 42\\\"\""'; echo $?
1
$ bash -e -c ': | eval "(exit 42)"'; echo $?
1


Neither `dash` not `zsh` nor `ksh` show this behavior:

$ dash -ec ':|eval "(exit 42)"'; echo $?
42
$ zsh  -ec ':|eval "(exit 42)"'; echo $?
42
$ ksh -e -c ': | eval "(exit 42)"'
42


= Variants =
* `errexit` is required to show the bug
* using a pipe is required to show the bug
* the `exit 42` must happen in another sub-process, either `(exit 42)` or `sh
-c 'exit 42'` but not simply `exit 42`
* `set -o pipefail` does not make any difference
* `shopt -s lastpipe` fixes it

= bisect =
I have testes old version of bash and the bug seems to start after 4.0-rc1:

$ podman run --rm docker.io/library/bash:3.0-alpine3.22 bash -e -c ': | eval
"(exit 42)"'; echo $?
42
$ podman run --rm docker.io/library/bash:3.1-alpine3.22 bash -e -c ': | eval
"(exit 42)"'; echo $?
42
$ podman run --rm docker.io/library/bash:3.2-alpine3.22 bash -e -c ': | eval
"(exit 42)"'; echo $?
42
$ podman run --rm docker.io/library/bash:4.0-alpine3.22 bash -e -c ': | eval
"(exit 42)"'; echo $?
1
$ podman run --rm docker.io/library/bash:5.0-alpine3.22 bash -e -c ': | eval
"(exit 42)"'; echo $?
1


== git bisect ==
I have run `git bisect` to further shrink the set of changes, which shows the
but to show up between `4.0-rc1` and `4.0`:

$ cat ../bash.bisect
#!/bin/sh
git clean -fdx || exit 2
[ -x ./configure ] || autoconf || exit 2
./configure || exit 125
make -j"$(nproc)" || exit 125
./bash -e -c ': | eval "(exit 42)"'
rv=$?
echo "*** rv=$rv ***"
git reset --hard
[ $rv -eq 42 ]

$ git bisect reset
$ git bisect start bash-4.0 bash-3.2-alpha
$ git bisect run ../bash.bisect
…
# bad: [89a92869e56aba4e4cab2d639c00a86f0545c862] Bash-4.0 patchlevel 38
# good: [e7f1978acfd2125b69bca36994882a1333607739] commit bash-20060706
snapshot
git bisect start 'bash-4.0' 'bash-3.2-alpha'
# skip: [7117c2d221b2aed4ede8600f6a36b7c1454b4f55] Imported from
../bash-2.05b.tar.gz.
git bisect skip 7117c2d221b2aed4ede8600f6a36b7c1454b4f55
# good: [0628567a28f3510f506ae46cb9b24b73a6d2dc5d] Imported from
../bash-3.2.tar.gz.
git bisect good 0628567a28f3510f506ae46cb9b24b73a6d2dc5d
# skip: [3185942a5234e26ab13fa02f9c51d340cec514f8] Imported from
../bash-4.0-rc1.tar.gz.
git bisect skip 3185942a5234e26ab13fa02f9c51d340cec514f8
# bad: [17345e5ad288f7543b77b23a25aa380eacc279f2] Imported from
../bash-4.0.tar.gz.
git bisect bad 17345e5ad288f7543b77b23a25aa380eacc279f2
# skip: [f1be666c7d78939ad775078d290bec2758fa29a2] Imported from
../bash-3.2.48.tar.gz.
git bisect skip f1be666c7d78939ad775078d290bec2758fa29a2
# only skipped commits left to test
# possible first bad commit: [17345e5ad288f7543b77b23a25aa380eacc279f2]
Imported from ../bash-4.0.tar.gz.
# possible first bad commit: [3185942a5234e26ab13fa02f9c51d340cec514f8]
Imported from ../bash-4.0-rc1.tar.gz.
# possible first bad commit: [f1be666c7d78939ad775078d290bec2758fa29a2]
Imported from ../bash-3.2.48.tar.gz.
# good: [3185942a5234e26ab13fa02f9c51d340cec514f8] Imported from
../bash-4.0-rc1.tar.gz.
git bisect good 3185942a5234e26ab13fa02f9c51d340cec514f8
# first bad commit: [17345e5ad288f7543b77b23a25aa380eacc279f2] Imported from
../bash-4.0.tar.gz.


== manual cherry-picking ==

I have then re-applied parts of 4.0 on top of 4.0-rc1 until I found the
relevant diff, which changes the behaviour of `execute_command_internal` and
introduces the bug. Removing the following lines from
`execute_command_internal()` in `execute_cmd.c` line 618-623 returns the old
"good" behavior:

-             if (user_subshell && ignore_return == 0 && invert == 0 &&
exit_immediately_on_error && exec_result != EXECUTION_SUCCESS)
-               {
-                 last_command_exit_value = exec_result;
-                 run_pending_traps ();
-                 jump_to_top_level (ERREXIT);
-               }


I have also inserted some debug `fprintf()` to show the values at the point of
execution:

> 610> user_subshell=1 was_error_trap=0 ignore_return=0 invert=0
> exit_immediately_on_error=1 exec_result=42
> 2184> was_error_trap=0 ignore_return=0 invert=0 exit_immediately_on_error=1
> exec_result=42








    _______________________________________________________
File Attachments:

Reverted hunk

Name: bash.diff                      Size: 2KiB
    <https://file.savannah.gnu.org/file/bash.diff?file_id=57614>



    AGPL NOTICE

These attachments are served by Savane. You can download the corresponding
source code of Savane at
https://savannah.gnu.org/source/savane-4b8598a5d17f272f0bb6fd029e74d5ef737ff864.tar.gz

    _______________________________________________________

Reply to this item at:

  <https://savannah.gnu.org/bugs/?67473>

_______________________________________________
Nachricht gesendet über Savannah
https://savannah.gnu.org/

Attachment: signature.asc
Description: PGP signature

Reply via email to