[Top][All Lists]

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

[PATCH] Exit status by no-argument `return' for function calls in trap h

From: Koichi Murase
Subject: [PATCH] Exit status by no-argument `return' for function calls in trap handlers
Date: Fri, 17 Apr 2020 02:21:09 +0900

Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -O2 -g -pipe -Wall -Werror=format-security
  -fstack-protector-strong -grecord-gcc-switches
  -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic
  -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection
  -Wno-parentheses -Wno-format-security
uname output: Linux chatoyancy 5.1.20-300.fc30.x86_64 #1 SMP Fri Jul
  26 15:03:11 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-redhat-linux-gnu

Bash Version: 5.0
Patch Level: 11
Release Status: release


  The behavior of no-argument `return' in trap handlers has been
  changed from Bash 4.4 to follow the description of POSIX.  Recently
  this behavior caused problems in my Bash script.  I am wondering
  whether this change actually matches with the behavior meant by
  POSIX because the change introduces unreasonable constraints in
  writing shell functions.

  For the condition of this special treatment of `return', POSIX says
  ``When return is executed in a trap action''.  Here are two possible
  interpretation: (A) `return's specified in the argument string of
  `trap' builtin are affected, or (B) all the `return's in entire
  runtime function-call tree in trap processing are affected.  I guess
  that POSIX wanted to provide a way to exit functions that received
  signals without changing the value of `$?'.  If that is the case,
  the POSIX should have meant (A).  However, the current Bash
  implementation behaves as if it follows the interpretation (B).

  I would like to hear what do you think of this.


  In the latest release of Bash 5.0.16 and in the current devel
  branch, when function calls in trap handlers are terminated by
  no-argument `return' builtin, the exit status is always the value of
  $? at the moment that the trap handler started.  This behavior was
  introduced in Bash 4.4.

  I noticed this behavior in debugging an infinite loop caused by
  SIGWINCH reported at https://github.com/akinomyoga/ble.sh/issues/48.
  The structure of the code related to the infinite loop can be
  summarized in the following small script.


  function check_loop_condition {
    if ((index++%10==0)); then
      echo index=$index
      return # *** return exit status of the previous command ***

    : do something
    return 0

  function update {
    local index=0
    while check_loop_condition; do :; done

  trap 'update' USR1
  kill -USR1 $$

  If the function `update' is called normally (outside of trap
  handlers), it will print the numbers {1..101..10} and terminate
  soon.  However, when it is called in trap handlers, it falls into an
  infinite loop in Bash 4.4+.  This is because the no-argument
  `return' in the function `check_loop_condition' always returns `$?'
  before the trap started regardless of the exit status of

  If all the `return's in the entire function-call tree are affected
  in trap processing as in the interpretation (B), one cannot reliably
  use no-argument `return' to return the last-command exit status.  To
  avoid the problem, one has to always write the exit status
  explicitly as `return $?', and there is no use case for no-argument
  `return' at all.  I don't think this is meant by POSIX which defines
  the behavior of no-argument `return' explicitly.  Or, maybe one
  cannot use shell functions in trap handlers.  Note that in my
  script, I need to re-render the terminal contents on SIGWINCH so
  that I need to run complicated shell programs implemented as shell
  functions.  POSIX does not prohibit the use of shell functions in
  trap handlers.

  Here I checked how the behavior was changed in Bash 4.4.  The
  related commits are 939d190e0 (commit bash-20140314 snapshot) and
  e2f12fdf5 (commit bash-20140321 snapshot).  The relevant ChangeLog
  is quoted as follows:

  >            3/11
  >            ----
  > builtins/common.c
  >   - get_exitstat: when running `return' in a trap action, and it is not
  >     supplied an argument, use the saved exit status in
  >     trap_saved_exit_value.  Fixes Posix problem reported by
  >     Eduardo A. Bustamante L坦pez <address@hidden>
  >            3/18
  >            ----
  > builtins/common.c
  >   - get_exitstat: update fix of 3/11 to allow the DEBUG trap to use the
  >     current value of $? instead of the value it had before the trap
  >     action was run.  This is one reason the DEBUG trap exists, and
  >     extended debug mode uses it.  Might want to do this only in Posix
  >     mode

  This change is made after the following discussion:


  Taking the following comment and the code example by the original
  reporter Eduardo, he seems to assume the interpretation (A), but
  what was actually implemented was the interpretation (B).

  > So as I read it, `action' refers to the whole string.

  Also, I have checked the behavior of other shells. `zsh', `ash'
  family (dash/ash, busybox sh) and `posh' does not implement the
  special treatment of `return' in trap handlers.  `ksh' family
  (ksh93, mksh) and `yash' implements the interpretation (B).  There
  is no existing implementation of (A).  But currently I still think
  the intepretation (A) is reasonable.  If there is rationale for the
  interpretation (B), I would like to know it.


  Here I provide a few test cases for the special treatment of
  no-argument `return'.  The following example demonstrates the
  behavior of no-argument `return' appearing directly in the trap
  argument.  The expected result is `exit=0' but not `exit=222' as
  `return' should return the exit status before the trap handler
  starts as required by POSIX.  Bash 4.3 and before outputs `exit=222'
  because it does not implement the special treatment.  Bash 4.4 and
  later ourputs `exit=0' as expected.


  setexit() { return "$1"; }
  trap 'setexit 222; return' USR1

  process() { kill -USR1 $$; }
  echo exit=$?

  The next example also demonstrates the behavior of no-argument
  `return' directly specified in the trap argument.  The expected
  result is to print `exit=0'.  Bash 4.3 and earlier outputs
  `exit=123', and Bash 4.4 and later outputs `exit=0' as expected by


  function setexit { return "$1"; }

  trap 'setexit 123; return' USR1

  function loop { while :; do :; done; }

  function get_loop_exit { loop; echo "exit=$?"; }

  { sleep 1; kill -USR1 $$; } &

  The third example is the case for `return' in function calls in trap
  processing.  If we adopt the interpretation A (B), the expected
  result is `A' (`B').  Bash 4.3 and earlier outputs `A', and Bash 4.4
  and later outputs `B'.


  check() { false; return; }
  handle() { check && echo B || echo A; }
  trap handle USR1
  kill -USR1 $$


  I attach a patch which changes the behavior to match with the more
  reasonable interpretation (A).  To detect the current nest level of
  returns, I initially thought about using the variable
  `return_catch_flags', but it turned out that the variable also
  counts the levels of `evalstring' so I decided to use `funcnest +
  sourcenest' instead.  The patch is tested with the above four cases.


Attachment: 0001-builtins-common-get_exitstat-check-nest-levels-of-ru.patch
Description: Binary data

reply via email to

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