gh-156378: Stop logging.config.listen() spinning on a truncated request - #156391
Open
ayaangazali wants to merge 1 commit into
Open
gh-156378: Stop logging.config.listen() spinning on a truncated request#156391ayaangazali wants to merge 1 commit into
ayaangazali wants to merge 1 commit into
Conversation
… request ConfigStreamHandler.handle() reads a 4-byte length and then loops until that many bytes arrive. At EOF recv() returns b'', so a client that announces a length and then closes without sending the body left the loop calling recv() forever, burning a CPU core per connection. Treat an empty read as end of stream and drop the partial request.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ConfigStreamHandler.handle()reads a 4-byte length and then loops until that many bytes have arrived:At end of stream
recv()returnsb'', sochunkstops growing while the loop condition stays true. A client that announces a length and then closes without sending the body leaves the handler callingrecv()forever.verifycannot help, since it only runs after this loop.This treats an empty read as end of stream and drops the partial request, which matches what the handler does with anything else it cannot act on.
Verifying
Announce one byte, then hang up, and watch process CPU while otherwise idle:
Before:
0.805seconds of CPU over 0.8 seconds of wall clock, so a core is pinned. After:0.001.test_loggingpasses in full, 292 tests.On the test
The added test waits for the handler thread to exit rather than measuring CPU, because a CPU threshold would be flaky on a loaded machine. It only fails if the thread never exits, so a slow box just makes it slower, not red. Verified both directions: with
Lib/logging/config.pyreverted it fails withAssertionError: the request handler did not exit after EOF, and it passes with the fix.I did not try to make the handler report the truncated request. It looked like a separate behaviour question from stopping the spin, and the surrounding code already discards input it cannot use without comment.
sorry if I've missed something here. I reproduced it from the issue, traced the loop myself, and used Claude Code to sanity check the fix and the test design before settling on waiting for thread exit instead of timing CPU. happy to change any of it. freshman in college, still learning my way around this codebase :)