https://bz.apache.org/bugzilla/show_bug.cgi?id=70288

            Bug ID: 70288
           Summary: HTTP/2 request-body completion can return true while
                    DATA remains buffered
           Product: Tomcat 11
           Version: unspecified
          Hardware: PC
                OS: other
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Connectors
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: -------

Created attachment 40236
  --> https://bz.apache.org/bugzilla/attachment.cgi?id=40236&action=edit
Standalone Maven reproducer project, one-line candidate fix, and validation
(save as repro.patch; git apply repro.patch)

Stream.StandardStreamInputBuffer.isRequestBodyFullyRead() can return true while
its input buffer contains unread DATA. An immediate second call returns false
without consuming any bytes. This can cause premature request-body completion
for HTTP/2 non-blocking consumers. The attached test directly exercises the
internal predicate; it does not itself reproduce a Servlet callback or an
HTTP exchange.

Environment: Oracle JDK 25.0.4+7-LTS-189, Windows 11, amd64. The 11.0.27
release is not currently offered in the Bugzilla Version selector.

The attached standalone Maven reproducer uses the real Tomcat Stream and state
machine. Only Http2UpgradeHandler is mocked. It needs no application framework,
server, network, database or load generator. The reproducer runs with JDK 25.

REPRODUCTION

Save the attached text as repro.patch. In a Git checkout, run:

    git apply repro.patch
    cd tomcat-http2-read-completion-reproducer
    mvn test

Expected: isRequestBodyFullyRead() remains false while one body byte is unread.
Actual on unmodified 11.0.27:

    first=true, unread=1, second=false, expectedFirst=false

The default command intentionally fails on the affected version. For a
diagnostic assertion of the faulty result over 20 forced interleavings, run:

    mvn -DexpectOriginalRace=true test

This diagnostic mode passes on the affected version and prints:

    {"iterations":20,"finishedWithUnreadData":20,"expectedOriginalRace":true}

The Maven test fork is bounded to 30 seconds. Dependencies and their versions
are pinned in the POM; no Tomcat binary or modified dependency is included.

FORCED INTERLEAVING AND CANDIDATE CAUSE

The stream is open, with content length 1 and an allocated empty input buffer.
The producer holds the stream state monitor so the consumer can be paused
while entering the state-machine check. With the original expression:

    return (inBuffer == null || inBuffer.position() == 0) && isInputFinished();

1. The consumer observes an empty buffer.
2. Before the consumer obtains the state monitor, the producer buffers one byte
   and processes END_STREAM.
3. The consumer obtains the monitor and observes that input has finished.
4. The method returns true although the byte remains buffered.

The real parser also writes DATA before publishing END_STREAM. The state
machine publishes and reads that transition through synchronized methods on
the same state object. However, in the current expression the empty-buffer
observation has already happened before acquiring that monitor.

The proposed one-line patch checks isInputFinished() first, then checks the
buffer. On the normal END_STREAM path, if the first check returns true, the
state-monitor acquire precedes the buffer observation. If it returns false,
the method conservatively reports that the body is not yet fully read.

This is a proposed correction for maintainer review, not a claim that all
HTTP/2 completion/reset paths have been independently audited.

PATCH AND VALIDATION

The attached unified diff targets the 11.0.27 Stream.java source. A short JUnit
wrapper invokes the standalone reproducer. The default test must pass after
the candidate fix and fail before it. See validation.txt for fresh package
verification, including the exact runtime and command results.

The source and patch are generic; they contain no product configuration or
customer data. The source expression was also checked in the published
11.0.x source branch on 2026-10-26. An authenticated Bugzilla search across
open and closed reports for isRequestBodyFullyRead and isFinished did not
identify a matching report. Bug 64974 concerns HTTP pipelining rather than
this HTTP/2 Stream completion interleaving.

-- 
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to