Skip to content

Issue #14403 - Serialize FCGI input buffer access - #15616

Closed
DragonFSKY wants to merge 1 commit into
jetty:jetty-12.1.xfrom
DragonFSKY:fix-14403-fcgi-input-buffer
Closed

Issue #14403 - Serialize FCGI input buffer access#15616
DragonFSKY wants to merge 1 commit into
jetty:jetty-12.1.xfrom
DragonFSKY:fix-14403-fcgi-input-buffer

Conversation

@DragonFSKY

Copy link
Copy Markdown
Contributor

Fixes #14403.

ServerFCGIConnection owns a single parser and input buffer, while request completion may run on a different server thread while onFillable() or parseAndFill() is still using that buffer. This can release the connection-owned buffer before input processing has completed.

Serialize those connection-level input-processing and completion paths with a per-connection AutoLock. Add a deterministic regression test that holds an in-progress fill and verifies completion waits before releasing the buffer.

Tests:

  • JDK 21 and JDK 25: ServerFCGIConnectionTest
  • JDK 25: ServerFCGIConnectionTest,HttpClientTest (24/24)
  • JDK 25: RoundRobinConnectionPoolTest#testMultiplexWithMaxUsage (6/6 transports)
  • Spotless and Checkstyle

@lorban lorban left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You added a very coarse lock and because of its broad scope, this happens to fix the test, or make it much less likely to reproduce (I haven't checked that).

You're freely calling non-local methods and you did not modify all the methods accessing the member variables under the lock's scope.

I'm sorry but this isn't a proper fix, but it looks more like a random attempt at throwing a lock at the problem and hoping that this will make the problem go away.

If you believe you figured out how threads interact with each other to make the reported problem happen, you should then figure out a proper locking strategy with minimal lock scopes.

// even if the buffer has been fully consumed because releaseInputBuffer()
// must be called as the last release for it to be able to null out the
// inputBuffer field exactly when the latter isn't used anymore.
if (parse(inputBuffer.getByteBuffer()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot call the parser with a lock held, this would lead to deadlocks.

else if (read == 0)
{
releaseInputBuffer();
fillInterested(fillableCallback);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling any method of another class (even a super one) with a lock held is an anti-pattern. All around Jetty's codebase we made sure to never do that.

else
{
releaseInputBuffer();
shutdown();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: you cannot call another class' method with a lock held.

// even if the buffer has been fully consumed because releaseInputBuffer()
// must be called as the last release for it to be able to null out the
// inputBuffer field exactly when the latter isn't used anymore.
if (parse(inputBuffer.getByteBuffer()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: you cannot call the parser with a lock held.

{
releaseInputBuffer();
if (failure == null)
fillInterested(fillableCallback);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: you cannot call another class' method with a lock held.

if (failure == null)
fillInterested(fillableCallback);
else
getFlusher().shutdown();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: you cannot call another class' method with a lock held.

@DragonFSKY

Copy link
Copy Markdown
Contributor Author

@lorban Thanks for the review. I reworked the fix in #15648 to defer application dispatch until ServerParser.parse() has returned and input-buffer bookkeeping is complete, without adding a connection-wide lock.

I am closing this PR in favor of the new one. Please feel free to let me know if you spot any issues.

@DragonFSKY DragonFSKY closed this Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RoundRobinConnectionPoolTest.testMultiplexWithMaxUsage() on FCGI is flaky

2 participants