This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new 4bdc1b0 Fix BZ 65118 - avoid an NPE when pruning streams 4bdc1b0 is described below commit 4bdc1b0c69d7a55a9def6b17a08573dce8e82f77 Author: Mark Thomas <ma...@apache.org> AuthorDate: Mon Feb 1 15:30:27 2021 +0000 Fix BZ 65118 - avoid an NPE when pruning streams The pruning process considers the parent/child relationship between streams. The parent/child relationship is part of the priority tree so the code needs to be holding the appropriate lock to ensure a consistent view. https://bz.apache.org/bugzilla/show_bug.cgi?id=65118 # Conflicts: # webapps/docs/changelog.xml --- .../apache/coyote/http2/Http2UpgradeHandler.java | 78 ++++++++++++---------- webapps/docs/changelog.xml | 8 +++ 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java index d8c8df7..6647624 100644 --- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java +++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java @@ -1218,40 +1218,23 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH // Step 1 // Iterator is in key order so we automatically have the oldest streams // first - for (AbstractNonZeroStream stream : streams.values()) { - // Never remove active streams - if (stream instanceof Stream && ((Stream) stream).isActive()) { - continue; - } - - if (stream.isClosedFinal()) { - // This stream went from IDLE to CLOSED and is likely to have - // been created by the client as part of the priority tree. - // Candidate for step 3. - candidatesStepThree.add(stream.getIdentifier()); - } else if (stream.getChildStreams().size() == 0) { - // Prune it - AbstractStream parent = stream.getParentStream(); - streams.remove(stream.getIdentifier()); - stream.detachFromParent(); - if (log.isDebugEnabled()) { - log.debug(sm.getString("upgradeHandler.pruned", connectionId, stream.getIdAsString())); - } - if (--toClose < 1) { - return; + // Tests depend on parent/child relationship between streams so need to + // lock on priorityTreeLock to ensure a consistent view. + synchronized (priorityTreeLock) { + for (AbstractNonZeroStream stream : streams.values()) { + // Never remove active streams + if (stream instanceof Stream && ((Stream) stream).isActive()) { + continue; } - // If removing this child made the parent childless then see if - // the parent can be removed. - // Don't try and remove Stream 0 as that is the connection - // Don't try and remove 'newer' streams. We'll get to them as we - // work through the ordered list of streams. - while (toClose > 0 && parent.getIdAsInt() > 0 && parent.getIdAsInt() < stream.getIdAsInt() && - parent.getChildStreams().isEmpty()) { - // This case is safe since we know parent ID > 0 therefore - // this isn't the connection - stream = (AbstractNonZeroStream) parent; - parent = stream.getParentStream(); + if (stream.isClosedFinal()) { + // This stream went from IDLE to CLOSED and is likely to have + // been created by the client as part of the priority tree. + // Candidate for step 3. + candidatesStepThree.add(stream.getIdentifier()); + } else if (stream.getChildStreams().size() == 0) { + // Prune it + AbstractStream parent = stream.getParentStream(); streams.remove(stream.getIdentifier()); stream.detachFromParent(); if (log.isDebugEnabled()) { @@ -1260,12 +1243,33 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH if (--toClose < 1) { return; } - // Also need to remove this stream from the step 2 list - candidatesStepTwo.remove(stream.getIdentifier()); + + // If removing this child made the parent childless then see if + // the parent can be removed. + // Don't try and remove Stream 0 as that is the connection + // Don't try and remove 'newer' streams. We'll get to them as we + // work through the ordered list of streams. + while (toClose > 0 && parent.getIdAsInt() > 0 && parent.getIdAsInt() < stream.getIdAsInt() && + parent.getChildStreams().isEmpty()) { + // This case is safe since we know parent ID > 0 therefore + // this isn't the connection + stream = (AbstractNonZeroStream) parent; + parent = stream.getParentStream(); + streams.remove(stream.getIdentifier()); + stream.detachFromParent(); + if (log.isDebugEnabled()) { + log.debug(sm.getString("upgradeHandler.pruned", connectionId, stream.getIdAsString())); + } + if (--toClose < 1) { + return; + } + // Also need to remove this stream from the step 2 list + candidatesStepTwo.remove(stream.getIdentifier()); + } + } else { + // Closed, with children. Candidate for step 2. + candidatesStepTwo.add(stream.getIdentifier()); } - } else { - // Closed, with children. Candidate for step 2. - candidatesStepTwo.add(stream.getIdentifier()); } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index e3bad55..b6e5fa5 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -104,6 +104,14 @@ issues do not "pop up" wrt. others). --> <section name="Tomcat 9.0.44 (markt)" rtext="in development"> + <subsection name="Coyote"> + <changelog> + <fix> + <bug>65118</bug>: Fix a potential <code>NullPointerException</code> when + pruning closed HTTP/2 streams from the connection. (markt) + </fix> + </changelog> + </subsection> </section> <section name="Tomcat 9.0.43 (markt)" rtext="release in progress"> <subsection name="Catalina"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org