Possible DOS in KeepAliveCache

Thomas Lußnig openjdk at suche.org
Mon Jul 6 22:13:13 UTC 2015


Hi,

i found after an high number of CLOSE_WAIT's in an high load production
environment that there is and problem with
sun.net.www.http.KeepAliveCache. There are two conditions when an
sun.net.www.http.HttpClient will be closed:
1) If the connection is detected to be closed by usage.
2) If the idleTimeout is expired.

We had an server that send an incorrect Header "Keep-Alive:
timeout=1200000, max=1000" and close the connection
much before the timeout.

This can cause an problem because there seems to be no limit for timeout
and the "run()" method in KeepAliveCache
does not detect if the client connection is in state CLOSE_WAIT.

I solved this by an "dirty" trick with " socket.sendUrgentData(0);".
Sadly that there is not option to use getsockopt - SO_ERROR to check if
the socket is closed.
Is there any clean way to find out if an socket is in state close_wait?
- Maybe check if we can read one byte (nonBlocking) because we are not
expecting data on idle connection.

Gruß Thomas Lußnig

p.s. My workaround

public void run() {
        do {
            try { Thread.sleep(LIFETIME); } catch (final
InterruptedException e) {}
            synchronized (this) {
                /* Remove all unused HttpClients.  Starting from the
bottom of the stack (the least-recently used first).
                 * REMIND: It'd be nice to not remove *all* connections
that aren't presently in use.
                 * One could have been added a second ago that's still
perfectly valid, and we're needlessly axing it.
                 * But it's not clear how to do this cleanly, and doing
it right may be more trouble than it's worth.
                 */
                final long currentTime = System.currentTimeMillis();
                final ArrayList<KeepAliveKey> keysToRemove = new
ArrayList<>();
                for (final KeepAliveKey key : keySet()) {
                    final ClientVector v = get(key);
                    synchronized (v) {
                        for (int i = 0; i < v.size(); i++) {
                            final KeepAliveEntry e = v.elementAt(i);
                            if ((currentTime - e.idleStartTime) > v.nap) {
                                e.hc.closeServer();
                            } else {
                                if(i>0) v.subList(0, i).clear();
                                break;
                            }
                        }
                        if(serverSocket != null) {
                            final ArrayList<KeepAliveEntry>
clientsToRemove = new ArrayList<>();
                            for (final KeepAliveEntry e : v) {
                                try {
                                    @SuppressWarnings("resource") final
Socket socket = (Socket)serverSocket.get(e.hc);
                                    try { socket.sendUrgentData(0); }
                                    catch(final Throwable t) {
                                        e.hc.closeServer();
                                        clientsToRemove.add(e);
                                        System.out.println("ClosedHttp:
"+socket.getLocalPort()+">"+socket.getRemoteSocketAddress()+" 
Timeout:"+v.nap+"/" + (currentTime-e.idleStartTime)+" => "+t.getMessage());
                                    }
                                } catch(final Throwable t) {
t.printStackTrace(System.out); }
                            }
                            for(final KeepAliveEntry e :
clientsToRemove) { v.remove(e); }
                        } // FOR
                        if (v.size() == 0) { keysToRemove.add(key); }
                    }
                }
                for(final KeepAliveKey key : keysToRemove) {
removeVector(key); }
            }
        } while (size() > 0);
        return;
    }


More information about the security-dev mailing list