Mark,

On 11/22/21 11:57, Mark Thomas wrote:
On 22/11/2021 15:36, Christopher Schultz wrote:
Mark,

Apologies for top-posting but this might get lost in the code below.

No problem.

This patch appears to:

1. Get the address from the just-accepted socket
2. Compare the address to the most-recently-accepted socket address (see #3)
  2a. Throw an error if the current and previous address are the same
3. Save a reference to the just-accepted socket address

Won't this be a problem if there is only one connection being handled, going in and out of the poll/accept cycle?

No. accept() only happens on initial connection.

Understood.

I haven't looked at the implementation of SocketAddress.equals and I may be confusing things a bit, but I would imagine on a mostly-idle server, this might cause an error:

1. Client connects from e.g. 127.0.0.1
2. Server accepts, stores 127.0.0.1
3. Client disconnects
4. Client connects from 127.0.0.1
2. Server accepts, throws error that 127.0.0.1 == 127.0.0.1

This would happen pretty frequently for connections through a reverse-proxy.

Is there a way to differentiate between "I've seen this address before" and "this is actually a duplicate?"

The address includes the remote port.

Aha, that should fix things, then. Adding the remote port should clear everything up.

Looking at InetSocketAddress.equals is clear that the (client) port number is indeed significant.

My observation is that the port that the client uses will increment for subsequent connections so in the scenario above previous address will not equal current address and Tomcat will continue as normal. > You might be able construct a scenario where you could get a legitimate duplication. My sense is that is even less likely that the bug so from that point of view having the defensive code is better than not having it.

So since accept() is only called once per connection (regardless of how many requests are processed through that connection), a client would have to make a connection, close it, get the TCP/IP stack to re-use the exact same local (outbound) port to connect to Tomcat, and *also* not make any additional new connections (as to clear-away the previously-accepted-socket address) in the meantime.

Yes, I agree this is ... unlikely.

The only situation I could see this happening in the real world would be to have a client use SO_REUSEADDR+SO_REUSEPORT and make two connections to each of two separate ports on the Tomcat server, with a shared Acceptor thread. I'm fairly sure we don't ever share Acceptor threads in Tomcat, so this situation is essentially impossible.

If we wanted to protect against legitimate duplication I think we'd have to add some sort of timing constraint. i.e. the duplication has to occur with some small number of milliseconds. I'm not sure how the cost/benefit analysis would work out for that sort of change.

+1 to it not being worth it. We could store the number of microseconds since since the last accept and allow a "repeat" if it's after a certain amount of time, but clock behavior on systems isn't always reliable and we'd never know how fast "too fast" might be.

Of course, the better option would be to get the bug fixed but my bug report has been dormant since I raised it.

Right?

OTOH, whether the bug is fixed is less relevant than whether or not admins accept the fix. :/

This workaround will probably have to remain in-place for the foreseeable future.

Since this is a Linux-only bug, would it be worth it to make this "tracking" optional, and only enable it on Linux JVMs? Or allow an administrator to disable it if they know they have the patch?

It seems like very little overhead -- especially because it's done only once per connection and not, for example once per request or (much worse) once-per-IO-event. So it's probably not worth the extra code unless it's easy to swap-out the implementation class of NIO[2]?Endpoint with DuplicateDetectorEndpoint or something like that.

If it's easy to do that, it might be worth wrapping the Endpoint with a decorator (requires significant refactoring) or using a subclass which overrides serverSocketAccept().

-chris

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to