System.exit deadlock if called within a hook

remi.catherinot at orange.com remi.catherinot at orange.com
Mon Apr 25 13:08:49 UTC 2022


Hi,

Exemple code to deadlock the JVM. Only Runtime.halt from within the JVM or a SIGKILL from outside stops it. Normal kills and Ctrl-C (which is a SIGTERM) fail to do so.

public class ExitInHookDemo {
            public static void main(String...args) {
                        Runtime.getRuntime().addShutdownHook(new Thread("hook") {
                                    public void run() { System.exit(1); }
});
            }
}

I've tried to more or less do a pure java patch in java.lang.Shutdown but the only way to have a real patch would be to have Thread.destroy still there and implemented or anything allowing Shutdown to destroy a thread. In essence, System.exit is a no return method call. So using Thread.stop which raise a ThreadDeath error is not enough (the method go back to the caller with the error). Only Thread.destroy has the contract of being a no return call. Yes the documentation said it is unsafe because it does not release locks & co, but right now that is exactly what is happening, so Thread.destroy limitations are acceptable. Throwing something that is not catchable, something legit to throw but not legit to catch, even by a catch Throwable, is not enough because it would still run finally blocks, which cannot happen for no return methods like System.exit and Runtime.halt: try { System.exit(0); /* code that should never be reached */ } catch (Throwable t) { /* code that should never be reached */ } finally { /* code that should also never be reached */ }

I've searched the known/documented bugs but I either missed the fact it was already reported, or I only got entries about removing Thread.destroy.

By the way, I'm not really asking to have Thread.destroy implemented were it is currently defined (a public method inside Thread). I mean at least java.lang.Shutdown should have a way to destroy Threads, even a private one. Threads destroyed that way should make threads stuck joining the destroyed one must be released.

I got the bug on java 1.8, oracle and openjdk ones. Not tested it one >=9 ones.

Here is what the approximation of a patch using pure java, but I'm no jdk expert and had no access to thread destruction, so it is not perfect. The code is from java.lang.Shutdown
    static void exit(int status) {
        boolean wasRunning = false; // < - - new var
        boolean runMoreFinalizers = false;
        synchronized (lock) {
            switch (state) {
            case RUNNING:       /* Initiate shutdown */
                state = HOOKS;
                wasRunning = true; // < - - first call to System.exit, the caller will run all of what is needed -hooks, maybe finalizers, etc.
                if (status != 0) runFinalizersOnExit = false; // < - - also moved the runFinalizersOnExit block to avoid having it changed/altered by several threads trying to exit with different status code
                break;
            case HOOKS:         /* Stall and halt */
                break;
            case FINALIZERS:
                if (status != 0) {
                    /* Halt immediately on nonzero status */
                    halt(status);
                } else {
                    /* Compatibility with old behavior:
                     * Run more finalizers and then halt
                     */
                    runMoreFinalizers = runFinalizersOnExit;
                }
                break;
            }
        }
        if (!wasRunning) Thread.currentThread().stop(); // < - - should be a destroy rather than a stop. This thread is not the 1st who called System.exit, and since it should never return, it must die asap. We are outside of the synchronized block so if any lock remains acquired by this thread, they are acquired by the caller which should have released them before calling exit on the 1st place. If the caller called us while having locks, it is a caller bug, not a Shutdown.exit one. Current implementation since it never returns (and yield the stuck bug I report) also never release any of the locks it has too
        // < - - beyond this line nothing has been modified
        if (runMoreFinalizers) {
            runAllFinalizers();
            halt(status);
        }
        synchronized (Shutdown.class) {
            /* Synchronize on the class object, causing any other thread
             * that attempts to initiate shutdown to stall indefinitely
             */
            sequence();
            halt(status);
        }
    }


Regards,
Ogrom.

_________________________________________________________________________________________________________________________

Ce message et ses pieces jointes peuvent contenir des informations confidentielles ou privilegiees et ne doivent donc
pas etre diffuses, exploites ou copies sans autorisation. Si vous avez recu ce message par erreur, veuillez le signaler
a l'expediteur et le detruire ainsi que les pieces jointes. Les messages electroniques etant susceptibles d'alteration,
Orange decline toute responsabilite si ce message a ete altere, deforme ou falsifie. Merci.

This message and its attachments may contain confidential or privileged information that may be protected by law;
they should not be distributed, used or copied without authorisation.
If you have received this email in error, please notify the sender and delete this message and its attachments.
As emails may be altered, Orange is not liable for messages that have been modified, changed or falsified.
Thank you.



More information about the core-libs-dev mailing list