'sh' may hang for ever as some java implementation hide SIGPIPE by default

This issue is archived. You can view it, but you can't modify it. Learn more

XMLWordPrintable

      Hi,

      When migrating our docker agent image from a RHEL 9 using JDK 22 or 23 to a RHEL 10 image using JDK 21 we have found a regression for some `sh` invocation that may hang forever. Basically, we have one shell script called by `sh` which eventually calls something like:

      ```

      RANDOM_STRING="$(tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1)"

      ```

      This works only if SIGPIPE isn't blocked, as we definitely expect the `head` command to immediately send a SIGPIPE to the `fold` command after one line as been echoed. If you run this in a "normal" shell, it works fine, however if you run this with SIGPIPE ignored, then you will see it hang for ever. On a recent distro, you can emulate this way:

      ```
      [amadeus@50ca067559e9]~% env --ignore-signal=SIGPIPE sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1"

      aa78

      ^C <--- here it hangs for ever until hitting control + C

      ```

      It happens that some java implementation do hide the SIGPIPE signal by default. This is the case of the JDK 21 found on RHEL 9/10/Fedora. You can easily reproduce that in some docker images based on CentOS/Fedora and run this trivial java program that is just a wrapper for system commands passed as argument:

      ```
      [amadeus@50ca067559e9]~% cat plop.java
      import java.io.BufferedReader;   
      import java.io.InputStreamReader;
      import java.io.IOException;

      class TestSystemCommand

      {
          public static void main(String[] args)
          {
              try
              {
                  Process process = Runtime.getRuntime().exec(args);
                  BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                  String line;

                  while ((line = reader.readLine()) != null)
                 

      {                 System.out.println(line);             }

                  reader.close();
              }
              catch (IOException e)
             

      {             e.printStackTrace();         }

          }
      }

      ```

      Which you can run like this:

      ```

      [amadeus@50ca067559e9]~% java plop.java sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1"

      7ow9

      ^C% <--- Hang for ever until hitting control + C
      ```

      If you run a `quay.io/fedora/fedora` container and `dnf install -y java-latest-openjdk-headless java-21-openjdk-headless` then you can see that there is at least with openjdk some difference of behavior between JDK 21 and JDK 24:

      ```

      [root@3711488feefd /]# /usr/lib/jvm/java-21-openjdk/bin/java plop.java sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1"
      5eco
      ^C[root@3711488feefd /]#  <--- Hangs for ever here
      [root@3711488feefd /]# /usr/lib/jvm/java-24-openjdk/bin/java plop.java sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1"
      o8qn
      [root@3711488feefd /]#
      ```

      When using a recent coreutils (coreutils >= 8.31, released in 2019), this issue can be fixed by wrapping the command with the `env` command like this:

      ```
      [root@3711488feefd /]# /usr/lib/jvm/java-21-openjdk/bin/java plop.java env --default-signal=SIGPIPE sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1"
      x0jw
      [root@3711488feefd /]# /usr/lib/jvm/java-24-openjdk/bin/java plop.java env --default-signal=SIGPIPE sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1"
      s864
      ```

      In order to cope with both old and new OS, where `env` might know the  `--default-signal` argument, this more complex wrapper can be used:

      ```
      [root@3711488feefd /]# /usr/lib/jvm/java-21-openjdk/bin/java plop.java sh -c 'if env --default-signal=SIGPIPE true 1>/dev/null 2>&1; then exec env --default-signal=SIGPIPE "$@"; else exec "$@"; fi' – sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1" 
      n3d7
      [root@3711488feefd /]# /usr/lib/jvm/java-24-openjdk/bin/java plop.java sh -c 'if env --default-signal=SIGPIPE true 1>/dev/null 2>&1; then exec env --default-signal=SIGPIPE "$@"; else exec "$@"; fi' – sh -c "tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n1" 
      ormg
      ```

      I will submit a pull request in the Github repo to wrap the call to `nohup` with the above described workaround.

      Cheers,
      Romain

            Assignee:
            Unassigned
            Reporter:
            Romain Geissler
            Archiver:
            Jenkins Service Account

              Created:
              Updated:
              Archived: