You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by "lbruun (JIRA)" <ji...@apache.org> on 2017/10/29 19:15:00 UTC

[jira] [Commented] (NETBEANS-58) NB IDE or NB Platform freeze on startup (proxy with Negotiate auth)

    [ https://issues.apache.org/jira/browse/NETBEANS-58?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16224149#comment-16224149 ] 

lbruun commented on NETBEANS-58:
--------------------------------

I've created a minimal Swing example to show off the problem:

{code:java}

public class MinimalSwing extends JFrame {

    public MinimalSwing() throws HeadlessException {
        setTitle("Minimal example");
        setSize(300, 200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }
    
    public static void main(String[] args) throws InterruptedException, InvocationTargetException {
        // Start the GUI
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                MinimalSwing app = new MinimalSwing();

            }
        });
        // Start the background thread simulating a network task
        Thread networkThread = new Thread(new NetworkTask());
        networkThread.start();
   }
    
    private static class NetworkTask implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(5000);
                
                // Simulate what the JDK does to us: 
                // we get called with a lock on the classloader.
                synchronized (Thread.currentThread().getContextClassLoader()) {
                    String answer = getUserInput();
                    System.out.println("User answered : " + answer);
                }
            } catch (InterruptedException | ExecutionException ex) {
            }
        }
    }
    
    private static String getUserInput() throws InterruptedException, ExecutionException {
        FutureTask<String> dialogTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return JOptionPane.showInputDialog("Input username/Password", null);
            }
        });
        SwingUtilities.invokeLater(dialogTask);
        return dialogTask.get();
    }   
}
{code}

If you run the above you'll first see the app being displayed. After exactly 5 seconds the whole app will freeze.  *BUT !*   In order to simulate what NetBeans does, otherwise the app won't freeze and everything will work perfectly, you must execute the JRE with {{-XX:+AlwaysLockClassLoader}}. Long story short: The classloaders in the JDK were changed in Java 7 to be _parallel capable_. Unfortunately this is not a capability that is inherited by default and NetBeans was created long before this. So NetBeans still has classloaders which synchronizes on the *whole classloader* instead of the more fine grained synchronization that has been used in JDK's out-of-the-box classloaders since Java 7. So the problem is really related to NetBeans classloaders which should use more fine fine grained synchronization akin to what the out-of-the-box classloaders in the JDK does. The JDK people are not likely to fix JDK-8068184 since the problem is related to NB only.

Proposal:
* Short term fix:  Workaround as proposed, i.e. do not attempt to prompt and let the Authenticator fail (return {{null}}) - if the lock is present. I'll come up with a PR shortly.
* Long term fix:   NETBEANS-106.




> NB IDE or NB Platform freeze on startup (proxy with Negotiate auth)
> -------------------------------------------------------------------
>
>                 Key: NETBEANS-58
>                 URL: https://issues.apache.org/jira/browse/NETBEANS-58
>             Project: NetBeans
>          Issue Type: Bug
>          Components: platform - Proxy
>    Affects Versions: 8.2, 9.0
>         Environment: Primarily Windows.
>            Reporter: phansson (lbruun)
>         Attachments: NETBEANS-58-workaround1.diff, netbeans.txt
>
>
> When any network operation is performed, such as attempting to contact NetBeans Update Center, the application (IDE or Platform) may freeze. Users will typically experience this on startup. It was reported in old bug tracker as [bug 248308|https://netbeans.org/bugzilla/show_bug.cgi?id=248308].
> The problem arises because of the fix JDK folks applied as a consequence of the reported [JDK-8032832 bug|https://bugs.openjdk.java.net/browse/JDK-8032832]. This fix wasn't very clever IMO: it puts a lock on the classloader, thus introducing a range of other problems, one of them being that NetNeans IDE or NetBeans Platform will likely freeze on startup when it attempts a network operation. The fact that their fix made things worse (while no doubt fixing the original issue) has been reported as [JDK-8068184|https://bugs.openjdk.java.net/browse/JDK-8068184].
> h3. WHEN DOES IT HAPPEN?
> As the lock is introduced for authentication of type 'Negotiate' it of course only happens if there's a network proxy on the path which uses this type of authentication. Also known as SPNEGO. This form of authentication is in my experience very common in corporate networks, in particular those that base themselves on the Microsoft stack. But a person on Oracle's own internal network, such as a JDK developer, is most likely not exposed to it. :-)
> There's another condition for it to happen: The JRE runtime must be unable to provide 'credentials' (a Kerberos token) to the network proxy on its own. SPNEGO is really designed to be seamless and promptless. Support for it was added in Java 6. But later on Microsoft tightened the desktop security around obtaining the so-called 'session token' and the JDK folks were never able to work around this (unlike the makers of Chrome, FF, Opera, etc). Therefore, in real-life, SPNEGO in the JRE on Windows is no longer promptless:  it will be forced to ask the user for credentials, thus negating the idea of SPNEGO. It is the prompting which causes the freeze. SPNEGO on Mac OS X and Linux is most likely working just fine and the bug will never be experienced.
> h3. HOW DO I KNOW IF I'M AFFECTED BY EXACTLY THIS BUG?
> This bug in this ticket is characterized by the fact that you'll always be able to find the following in your thread dump:
> {noformat}
>    at sun.net.www.protocol.http.NegotiateAuthentication.isSupported(NegotiateAuthentication.java:<lineno>)
>         - locked <OBJECTID> (a org.netbeans.ModuleManager$SystemClassLoader)
> {noformat}
> Note that the [Ctrl-Break method|https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr019.html] of obtaining a thread dump is favoured over jstack and other methods.
> h3. WHY DOES IT HAPPEN?
> There will be a lock held on the classloader object when the JRE's registered  Authenticator is invoked. If the Authenticator does work on another thread, that other thread has a need for some classloading and the current thread needs to wait for the result of that thread, then bum!, there's a deadlock between the two threads. This means the lock on the classloader will never be released and it will ultimately affect other threads, such as the AWT dispatch thread (aka Swing EDT) which will then also lock. Then you have what the user experiences as a freeze.
> The NB Platform's own Authenticator, {{NbAuthenticator}}, does exactly what I described and will thus be triggering the deadlock. More precisely it will happen when NbAuthenticator calls Keyring. Does this mean the NbAuthenticator does something wrong?  No, of course it doesn't. The real problem is the lock on the classloader. It is actually virtually impossible to design an Authenticator which doesn't trigger this problem. You cannot predict when classloading is needed. In fact it is very likely to be needed when application is still not "warm", i.e. during startup.
> h3. WORKAROUNDS
> *#1*
> If on Windows: Setting the following registry key:
> {{HKLM\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters\allowtgtsessionkey}}
> to {{true}} will allow the JRE to obtain the session key, thus the JDK's HTTP classes will have no need to invoke Authenticator, thus deadlock will not happen. But this registry key only has effect for users who are _not_ local admins and since it is a HKLM such user will need to ask his administrator to do this change. There's probably zero chance in a million that a corporate network administrator will allow this change. After all, Microsoft introduced the tightened security for a reason and the admin will rightly ask why only the JRE needs this and not Chrome, IE, FF, Opera and so on?
> *#2*
> Convice the JDK folks that they've made a mess of it with this lock. I've pursued this avenue too. I've done that on the JDK security-dev mailing list. I've pointed to similar bug tickets for Eclipse IDE and IDEA and I got the attention of Weijun Wang ("Max") of Oracle who promised he would look at it. But to be honest the JDK people have a lot of other things on their plate and a fix will take some time.
> UPDATE 29-OCT-2017:  To be fair to the JDK folks to issue only occurs with NB's own classloaders. So the chance they'll fix it at the JDK end is probably slim.
> Link: http://mail.openjdk.java.net/pipermail/security-dev/2017-August/016267.html
> *#3*
> Re-design the Authenticator in the Platform. As I cannot change the code in NB itself, I've created a workaround as a plugin. The recipe is described in [Comment 44|https://netbeans.org/bugzilla/show_bug.cgi?id=248308#c44] on the original NB bug ticket. This is really a workaround, not a fix. It will simply give up on attempting to obtain credentials if it discovers that it is likely to be in this deadlock scenario. Thus it leaves the application with no outbound network connectivity but it is still better than the freeze. It also alerts the user to the situation using a bubble notification. 
> Links:
> https://bitbucket.org/phansson/netbeansnetworkauthenticator
> https://bitbucket.org/phansson/netbeansnetworkauthenticator/wiki/JDK-8068184%20Workaround
> *#4*
> It may help to set proxy username/password _explicitly_ in the NB's Options panel. But this is not a solution I recommend. It means you must put your AD password into NetBeans IDE Options. It will of course be static so once your AD password changes then you must remember to change there as well. And for the user to configure this, it requires that he can actually start the IDE or Platform app in the first place ... which is often not the case because of the freeze.
> *#5* 
> Use a JRE prior to 8u20 or prior to 7u76. To most people this workaround is unacceptable.
> h3. CONCLUSION
> IMHO #3 is the most attractive solution for now. It doesn't exclude the user still doing #1 or #4 to get full benefit. The real solution is of course #2.
> .. and the very best solution long term is if JDK would have same support for SPNEGO on Windows as does 'regular' applications such as Chrome, FF, Opera, IE, Edge, etc. Then we wouldn't have the problem in the first place. This has been discussed intensively over the last 7-8 years but there's a somewhat religious tug of war between Sun/Oracle and Microsoft on the matter. The problem can be solved if the JDK would base itself on the Win32 SSPI api in this area, rather than the long-time deprecated Win32 LsaCallAuthenticationPackage API. We are getting slightly off topic here and not appropriate for a bug ticket. :-)



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)