United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6648001 Cancelling HTTP authentication causes subsequent deadlocks
JDK-6648001 : Cancelling HTTP authentication causes subsequent deadlocks

Details
Type:
Bug
Submit Date:
2008-01-08
Status:
Closed
Updated Date:
2011-03-08
Project Name:
JDK
Resolved Date:
2011-03-08
Component:
core-libs
OS:
generic,windows_xp
Sub-Component:
java.net
CPU:
x86,generic
Priority:
P2
Resolution:
Fixed
Affected Versions:
6u10,7
Fixed Versions:

Related Reports
Backport:
Backport:
Backport:
Relates:
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description
During investigation of 6647956 (Browser authenticator not working correctly in new Java Plug-In) it was discovered that it is very easy to cause the HTTP authentication code in the core JRE to deadlock. The steps to reproduce the failure are as follows:

1. Install the JRE. This failure has been reproduced as far back as 1.4.2_16 and may even be reproducible in earlier JREs.
2. Use the Java Control Panel to enable the Java Console.
3. Navigate to the test case for 6647956 (http://j2se.east.sun.com/deployment/www/tests/1.6.0_10/6647956/).
4. Use the second link to open the test applet. (Do not navigate to the topmost link, and do not enter the username or password.)
5. Click "Cancel".
6. Click the browser's back button.
7. Navigate again to the applet.

The applet will fail to load because it is deadlocked waiting for a notification that will never arrive:

"thread applet-AuthApplet-2" prio=4 tid=0x0b3dd800 nid=0x3a8 in Object.wait() [0x0bbcf000..0x0bbcfb94]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x03054848> (a java.util.HashMap)
	at java.lang.Object.wait(Object.java:485)
	at sun.net.www.protocol.http.AuthenticationInfo.requestIsInProgress(AuthenticationInfo.java:117)
	- locked <0x03054848> (a java.util.HashMap)
	at sun.net.www.protocol.http.AuthenticationInfo.getServerAuth(AuthenticationInfo.java:258)
	at sun.net.www.protocol.http.HttpURLConnection.getServerAuthentication(HttpURLConnection.java:1640)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1087)
	- locked <0x02a12cf8> (a sun.net.www.protocol.http.HttpURLConnection)
	at AuthApplet.start(AuthApplet.java:20)
	at sun.plugin2.applet.Applet2Manager$AppletExecutionRunnable.run(Applet2Manager.java:1508)
	at java.lang.Thread.run(Thread.java:619)

It is incredible that such an obvious bug was not discovered until now. The fix should be applied and immediately backported to the latest update releases for all applicable release trains.

                                    

Comments
SUGGESTED FIX

This fix was produced and tested against a 1.6.0_10 workspace but should be obviously applicable to earlier releases. The affected files are in the sun.net.www.protocol.http package.

------- AuthenticationInfo.java -------
*** //C/Users/kbr/forte4j/platform/intel-win/bin/util/tmp/sccs.000292	Mon Jan  7 20:36:51 2008
--- AuthenticationInfo.java	Mon Jan  7 20:34:34 2008
***************
*** 9,16 ****
--- 9,19 ----
  
  import java.io.*;
  import java.net.*;
+ import java.util.ArrayList;
  import java.util.Hashtable;
+ import java.util.Iterator;
  import java.util.LinkedList;
+ import java.util.List;
  import java.util.ListIterator;
  import java.util.Enumeration;
  import java.util.HashMap;
***************
*** 125,134 ****
      /* signal completion of an authentication (whether it succeeded or not) 
       * so that other threads can continue. 
       */
!     static private void requestCompleted (String key) {
  	synchronized (requests) {
! 	    boolean waspresent = requests.remove (key) != null;
! 	    assert waspresent;
  	    requests.notifyAll();
  	}
      }
--- 128,147 ----
      /* signal completion of an authentication (whether it succeeded or not) 
       * so that other threads can continue. 
       */
!     static private void requestCompleted () {
  	synchronized (requests) {
!             // Remove any outstanding requests owned by the current thread
!             List/*<String>*/ keys = new ArrayList();
!             Thread current = Thread.currentThread();
!             for (Iterator iter = requests.keySet().iterator(); iter.hasNext(); ) {
!                 String key = (String) iter.next();
!                 if (requests.get(key) == current) {
!                     keys.add(key);
!                 }
!             }
!             for (Iterator iter = keys.iterator(); iter.hasNext(); ) {
!                 requests.remove(iter.next());
!             }
  	    requests.notifyAll();
  	}
      }
***************
*** 314,325 ****
  	endAuthRequest();
      }
  
!     void endAuthRequest () {
  	if (!serializeAuth) {
  	    return;
  	}
      	synchronized (requests) {
! 	    requestCompleted (cacheKey(true));
  	}
      }
  
--- 327,338 ----
  	endAuthRequest();
      }
  
!     static void endAuthRequest () {
  	if (!serializeAuth) {
  	    return;
  	}
      	synchronized (requests) {
! 	    requestCompleted ();
  	}
      }
  

------- HttpURLConnection.java -------
*** //C/Users/kbr/forte4j/platform/intel-win/bin/util/tmp/sccs.000292	Mon Jan  7 20:36:51 2008
--- HttpURLConnection.java	Mon Jan  7 20:22:43 2008
***************
*** 1266,1277 ****
  	    }
  	    throw e;
  	} finally {
! 	    if (respCode == HTTP_PROXY_AUTH && proxyAuthentication != null) {
! 		proxyAuthentication.endAuthRequest();
! 	    } 
! 	    else if (respCode == HTTP_UNAUTHORIZED && serverAuthentication != null) {
! 		serverAuthentication.endAuthRequest();
! 	    }
  	}
      }
  
--- 1266,1273 ----
  	    }
  	    throw e;
  	} finally {
!             // Notify other threads that our authentication requests are complete
!             AuthenticationInfo.endAuthRequest();
  	}
      }
  
***************
*** 1425,1433 ****
  				      statusLine + "\"");
  	    }
  	} finally  {
! 	    if (respCode == HTTP_PROXY_AUTH && proxyAuthentication != null) {
! 		proxyAuthentication.endAuthRequest();
! 	    } 
  	}
  
  	// restore original request headers
--- 1421,1427 ----
  				      statusLine + "\"");
  	    }
  	} finally  {
!             AuthenticationInfo.endAuthRequest();
  	}
  
  	// restore original request headers
                                     
2008-01-08
EVALUATION

Yes, this is a bug. The suggested fix would work, except that it would remove all requests belonging to the current thread. Normally, there is only one request at a time per-thread, but because we provide a number of callback APIs (eg. Authenticator and ResponseCache), it is possible for multiple HTTP requests to occur re-entrantly. The plugin does actually use this capability (with the ResponseCache).

We have another fix which also works, but by exposing the key used in the authentication cache outside of AuthenticationInfo, this allows all requests to be terminated correctly, inlcuding ones, that have no username/password returned.
                                     
2008-01-09
EVALUATION

changeset:
  http://hg.openjdk.java.net/jdk7/tl/jdk/rev/69002275e0e2
                                     
2010-04-06



Hardware and Software, Engineered to Work Together