Control server: no socket timeout and unlimited connections allow thread exhaustion #1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
ControlServer.startServer()callsstart(0, true)(ControlServer.kt:74). NanoHTTPD only callsSocket.setSoTimeout()when the timeout argument is> 0— verified in the 2.3.1 bytecode (ServerRunnable.run:if (timeout <= 0) skip setSoTimeout). With0no read deadline is ever set, so reads block forever.NanoHTTPD spawns one unbounded thread per accepted connection.
RemoteSocket.onOpen()(ControlServer.kt:175) adds every socket toclientsunconditionally, and unauthorized non-hellomessages are silently dropped while the connection stays open — there is no handshake timeout and no connection cap.Failure scenario
Any host on the LAN opens connections to
:8765without ever sendinghello. Each pins a thread that is never reaped. A few thousand connections exhaust threads/FDs and the control server (and possibly the app) stops responding for legitimate users. No authentication is needed to trigger this.The same leak happens accidentally: a phone that drops Wi-Fi mid-handshake leaves a thread pinned forever.
Suggested fix
start(NanoHTTPD.SOCKET_READ_TIMEOUT, true)so accepted sockets get a read deadlineFound by multi-agent code review; verified against nanohttpd 2.3.1 bytecode and current
ControlServer.kt.Fixed in
dd612fc, shipped in v0.9.0.startServer()now usesstart(NanoHTTPD.SOCKET_READ_TIMEOUT, true), so accepted sockets get a read deadline instead of blocking forever.maintainClients(): it drops any socket that has not authenticated within 10 s, alongside the existing ping/reap.onOpen()refuses connections beyondMAX_CLIENTS = 8and closes them with a policy-violation frame.A stalled or hostile connection can therefore no longer pin a thread indefinitely.