Runtime.exec and SIGPIPE + SIG_IGN
Roger Riggs
roger.riggs at oracle.com
Tue Apr 29 18:07:16 UTC 2025
Hi Thomas, Archie,
Generally, I would agree that a bit more cleanup/resetting of the signal
state makes sense.
There is general unease about changing this kind of global state that
has been stable for a long time.
An application that expects to change operating system state before
calling exec and expecting it to be retained/passed to the child is
expecting too much.
A change here should also be reviewed by Hotspot, signals are a
sensitive area for them too.
Regards, Roger
On 4/29/25 10:55 AM, Thomas Stüfe wrote:
> Hi Archie!
>
> On Tue, Apr 29, 2025 at 4:17 PM Archie Cobbs <archie.cobbs at gmail.com>
> wrote:
>
> This reminds me of a bug I saw once decades ago: the JVM was not
> setting the close-on-exec flag on new file descriptors, so child
> processes were receiving copies of "leaked" file descriptors and
> keeping them open indefinitely, causing the system to eventually
> running out of file descriptors and crash.
>
>
> I remember. We fixed that in jspawnhelper.
>
> The moral of that story was that control of the "process
> environment" of a new process (i.e., which file descriptors are
> open, signal handlers installed, etc.) created via the usual
> fork/exec sequence clearly belongs to the bit of code that is
> spawning that process for whatever reason: any inheritable part of
> the process environment that the JVM might mess around with must
> be reset before spawning child processes.
>
>
> Agreed.
>
> So if some native code wants to configure SIGPIPE for SIG_IGN and
> then fork/exec a child process that inherits that setting, then fine.
>
> But if the JVM itself is doing its fork/exec for some unrelated
> purpose (e.g., Runtime.exec()), it has the right and
> responsibility to clean up the environment that the child process
> is going to inherit to protect it from any changes due to the JVM
> process, including native code.
>
> So it seems like there are two possible bugs here:
>
> 1. Some JNI code links in a library that changes the (global)
> setting for SIGPIPE. Is that allowed by the JNI specification?
> Does the specification even say? It seems like this is not really
> kosher, but anyway it happens to work by coincidence because
> SIG_IGN and javaSignalHandler do the same thing (i.e., discard the
> signal).
>
>
> To my knowledge the JNI spec says nothing about signals.
>
> 2. The JVM is not completely scrubbing the child's process
> environment when it spawns a new process as it should: All signal
> handlers should be reset to their default values (unless there is
> some JVM-specific reason to set them differently).
>
> It seems like #2 is a valid bug or at least a valid improvement,
> and whether #1 is a bug depends on what you believe JNI libraries
> are officially allowed to do.
>
>
> I agree. Note that we also have a slight inconsistency depending on
> the order of third-party signal handler installation:
>
> 1) In a scenario like the one I described we start java, it installs
> the SIGPIPE handler, which then gets preplaced by the third-party lib
> with SIG_IGN, resulting in child processes running with SIG_IGN for
> SIGPIPE.
> 2) If, however,r the libjvm.so gets embedded into a custom launcher,
> which already had set SIG_IGN for SIGPIPE, the JVM will replace that
> handler with its own, resulting in child processes running with SIG_DFL.
>
> This seems a bit arbitrary to me and seems to support the view that
> this behavior is not intended.
>
> -Archie
>
> On Tue, Apr 29, 2025 at 4:05 AM Thomas Stüfe
> <thomas.stuefe at gmail.com> wrote:
>
> Hi,
>
> I would like to gauge opinions on whether the following
> scenario is a bug to fix or whether to accept it as standard
> behavior.
>
> ---
>
> A customer has the following problem:
>
> - The JVM invokes a third-party JNI library that sets the
> signal disposition of SIGPIPE to SIG_IGN (Boo! in this case,
> it is the FIPS nspr library, which does this unconditionally.)
> - The JVM then spawns child processes
> - All child processes now ignore SIGPIPE, which leads to
> failures in automation scripts
>
> I was surprised. I always assumed the signal disposition of
> all signals would be reset to SIG_DFL when exec'ing. However,
> seems I was wrong when it came to SIG_IGN. Posix documents for
> execve() states [1]:
>
> 1) " Signals set to the default action (SIG_DFL) in the
> calling process image shall be set to the default action in
> the new process image."
>
> and
>
> 2) "Except for SIGCHLD, signals set to be ignored (SIG_IGN) by
> the calling process image shall be set to be ignored by the
> new process image."
>
> and
>
> 3) "Signals set to be caught by the calling process image
> shall be set to the default action in the new process image
> (see /<signal.h>/
> <https://urldefense.com/v3/__https://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html__;!!ACWV5N9M2RV99hQ!JGJYVEFhN3cSMzW41RLH_PK_mMddc3HnFEtCKYUXztE2-_9cjl_tIkMLO3y5_vav3Pt2ARimbCm44kuH8n_No6q2$>)."
>
> (2) and (3) are the interesting parts. (2) means that if the
> parent process ignores SIGPIPE, child processes will also
> ignore SIGPIPE. (3) means that if the parent process has a
> custom handler installed for SIGPIPE, child processes will be
> started with SIG_DFL (default action) for SIGPIPE. The default
> action for SIGPIPE is "terminate process".
>
> The libjvm handles SIGPIPE. We install our
> `javaSignalHandler`. We do this to eat up and ignore SIGPIPE.
> That we "manually" ignore the signal beside the point here -
> what counts is that we set a custom signal handler for
> SIGPIPE. Therefore, on execve, we behave according to rule (3)
> and start the child with SIG_DFL as SIGPIPE disposition. As it
> should be.
>
> If third-party code sets the handler to SIG_IGN as in this
> example, exeve will behave according to rule (2) and the child
> will start with SIG_IGN as SIGPIPE disposition.
>
> The libjig can be used to workaround this scenario, but I
> wonder if that is more of an accident. The libjsig.so will
> preserve the JVM's SIGPIPE handler even if third-party code
> attempts to set it to SIG_IGN. That means that exeve still
> behaves according to rule (2): sets child's SIGPIPE
> disposition to SIG_DFL.
>
> ----
>
> But I wonder whether this should not be considered a bug to
> fix regardless of the jsig.so workaround? In jspawnhelper, we
> clean the environment from various effects when exec'ing;
> among other things, we reset the signal block mask for the
> process. The "ignore" state of processes could be considered
> along the same line. We could reset all signal handlers to
> SIG_DFL before execing the child.
>
> I know that this area is super-tricky and problems are
> notoriously difficult to analyze; we should therefore be
> extremely careful not to break downward compatibility. Still,
> what do people think? Should be fix this in jspawnhelper?
>
> Thanks, Thomas
>
> (cc'ing Roger)
>
> [1]
> https://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
> <https://urldefense.com/v3/__https://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html__;!!ACWV5N9M2RV99hQ!JGJYVEFhN3cSMzW41RLH_PK_mMddc3HnFEtCKYUXztE2-_9cjl_tIkMLO3y5_vav3Pt2ARimbCm44kuH8vLbF0yj$>
>
>
>
> --
> Archie L. Cobbs
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20250429/b06a0146/attachment-0001.htm>
More information about the core-libs-dev
mailing list