-
Notifications
You must be signed in to change notification settings - Fork 96
Description
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)
reader.close();
}
catch (IOException e)
}
}
```
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
Originally reported by
romaingeissler1a, imported from: 'sh' may hang for ever as some java implementation hide SIGPIPE by default
- status: Open
- priority: Major
- component(s): durable-task-plugin
- resolution: Unresolved
- votes: 0
- watchers: 2
- imported: 2025-12-09
Raw content of original issue
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