From 30fc637f17712829c2482d98ce46887caedb3e0d Mon Sep 17 00:00:00 2001 From: wangkaiqiang Date: Fri, 9 Aug 2024 15:07:07 +0800 Subject: [PATCH] fix CVE-2024-22201 --- ...-2-connection-not-closed-after-idle-.patch | 706 ++++++++++++++++++ jetty.spec | 6 +- 2 files changed, 711 insertions(+), 1 deletion(-) create mode 100644 0003-Fixes-11259-HTTP-2-connection-not-closed-after-idle-.patch diff --git a/0003-Fixes-11259-HTTP-2-connection-not-closed-after-idle-.patch b/0003-Fixes-11259-HTTP-2-connection-not-closed-after-idle-.patch new file mode 100644 index 00000000..ac15f9eb --- /dev/null +++ b/0003-Fixes-11259-HTTP-2-connection-not-closed-after-idle-.patch @@ -0,0 +1,706 @@ +From 0839a208cdc3fcfe25206a77af59ba9fda260188 Mon Sep 17 00:00:00 2001 +From: Simone Bordet +Date: Mon, 15 Jan 2024 17:29:05 +0100 +Subject: [PATCH] Fixes #11259 - HTTP/2 connection not closed after idle + timeout when TCP congested. (#11267) + +Now upon the second idle timeout, the connection is forcibly closed. +Fixed also similar problem in HTTP/3. + +Signed-off-by: Simone Bordet +--- + .../org/eclipse/jetty/http2/HTTP2Session.java | 31 ++-- + .../jetty/http2/tests/IdleTimeoutTest.java | 61 +++++++- + .../org/eclipse/jetty/http3/HTTP3Session.java | 66 +++----- + .../jetty/http3/tests/IdleTimeoutTest.java | 148 ++++++++++++++++++ + .../jetty/quic/common/QuicSession.java | 12 +- + .../quic/server/QuicServerConnector.java | 14 +- + .../quic/server/ServerQuicConnection.java | 14 +- + .../jetty/quic/server/ServerQuicSession.java | 2 +- + .../eclipse/jetty/server/IdleTimeoutTest.java | 103 ++++++++++++ + 9 files changed, 385 insertions(+), 66 deletions(-) + create mode 100644 jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/IdleTimeoutTest.java + create mode 100644 jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/IdleTimeoutTest.java + +diff --git a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java +index 53319a13b7..0b0a753d24 100644 +--- a/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java ++++ b/jetty-core/jetty-http2/jetty-http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Session.java +@@ -1909,6 +1909,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session + { + String reason = "idle_timeout"; + boolean notify = false; ++ boolean terminate = false; + boolean sendGoAway = false; + GoAwayFrame goAwayFrame = null; + Throwable cause = null; +@@ -1923,10 +1924,9 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session + return false; + notify = true; + } +- +- // Timed out while waiting for closing events, fail all the streams. + case LOCALLY_CLOSED -> + { ++ // Timed out while waiting for closing events, fail all the streams. + if (goAwaySent.isGraceful()) + { + goAwaySent = newGoAwayFrame(ErrorCode.NO_ERROR.code, reason); +@@ -1935,7 +1935,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session + goAwayFrame = goAwaySent; + closed = CloseState.CLOSING; + zeroStreamsAction = null; +- failure = cause = new TimeoutException("Session idle timeout expired"); ++ failure = cause = newTimeoutException(); + } + case REMOTELY_CLOSED -> + { +@@ -1944,17 +1944,21 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session + goAwayFrame = goAwaySent; + closed = CloseState.CLOSING; + zeroStreamsAction = null; +- failure = cause = new TimeoutException("Session idle timeout expired"); +- } +- default -> +- { +- if (LOG.isDebugEnabled()) +- LOG.debug("Already closed, ignored idle timeout for {}", HTTP2Session.this); +- return false; ++ failure = cause = newTimeoutException(); + } ++ default -> terminate = true; + } + } + ++ if (terminate) ++ { ++ if (LOG.isDebugEnabled()) ++ LOG.debug("Already closed, ignored idle timeout for {}", HTTP2Session.this); ++ // Writes may be TCP congested, so termination never happened. ++ flusher.abort(newTimeoutException()); ++ return false; ++ } ++ + if (notify) + { + boolean confirmed = notifyIdleTimeout(HTTP2Session.this); +@@ -1973,6 +1977,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session + return false; + } + ++ private TimeoutException newTimeoutException() ++ { ++ return new TimeoutException("Session idle timeout expired"); ++ } ++ + private void onSessionFailure(int error, String reason, Callback callback) + { + GoAwayFrame goAwayFrame; +@@ -2036,7 +2045,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements Session + + private void sendGoAwayAndTerminate(GoAwayFrame frame, GoAwayFrame eventFrame) + { +- sendGoAway(frame, Callback.from(Callback.NOOP, () -> terminate(eventFrame))); ++ sendGoAway(frame, Callback.from(() -> terminate(eventFrame))); + } + + private void sendGoAway(GoAwayFrame frame, Callback callback) +diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/IdleTimeoutTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/IdleTimeoutTest.java +index d69ac6e265..97e38e120e 100644 +--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/IdleTimeoutTest.java ++++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/IdleTimeoutTest.java +@@ -13,7 +13,11 @@ + + package org.eclipse.jetty.http2.tests; + ++import java.net.InetSocketAddress; + import java.nio.ByteBuffer; ++import java.nio.channels.SelectionKey; ++import java.nio.channels.SocketChannel; ++import java.time.Duration; + import java.util.concurrent.CountDownLatch; + import java.util.concurrent.TimeUnit; + import java.util.concurrent.TimeoutException; +@@ -33,8 +37,11 @@ import org.eclipse.jetty.http2.frames.DataFrame; + import org.eclipse.jetty.http2.frames.GoAwayFrame; + import org.eclipse.jetty.http2.frames.HeadersFrame; + import org.eclipse.jetty.http2.frames.ResetFrame; ++import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; + import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; + import org.eclipse.jetty.io.Content; ++import org.eclipse.jetty.io.ManagedSelector; ++import org.eclipse.jetty.io.SocketChannelEndPoint; + import org.eclipse.jetty.server.Handler; + import org.eclipse.jetty.server.HttpConfiguration; + import org.eclipse.jetty.server.Request; +@@ -50,6 +57,7 @@ import org.junit.jupiter.api.Test; + + import static org.awaitility.Awaitility.await; + import static org.hamcrest.MatcherAssert.assertThat; ++import static org.hamcrest.Matchers.is; + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertFalse; + import static org.junit.jupiter.api.Assertions.assertNull; +@@ -747,10 +755,10 @@ public class IdleTimeoutTest extends AbstractTest + await().atMost(5, TimeUnit.SECONDS).until(() -> ((HTTP2Session)client).updateSendWindow(0), Matchers.greaterThan(0)); + + // Wait for the server to finish serving requests. +- await().atMost(5, TimeUnit.SECONDS).until(handled::get, Matchers.is(0)); +- assertThat(requests.get(), Matchers.is(count - 1)); ++ await().atMost(5, TimeUnit.SECONDS).until(handled::get, is(0)); ++ assertThat(requests.get(), is(count - 1)); + +- await().atMost(5, TimeUnit.SECONDS).until(responses::get, Matchers.is(count - 1)); ++ await().atMost(5, TimeUnit.SECONDS).until(responses::get, is(count - 1)); + } + + @Test +@@ -837,6 +845,53 @@ public class IdleTimeoutTest extends AbstractTest + assertTrue(responseLatch.await(5, TimeUnit.SECONDS)); + } + ++ @Test ++ public void testIdleTimeoutWhenCongested() throws Exception ++ { ++ long idleTimeout = 1000; ++ HTTP2CServerConnectionFactory h2c = new HTTP2CServerConnectionFactory(new HttpConfiguration()); ++ prepareServer(h2c); ++ server.removeConnector(connector); ++ connector = new ServerConnector(server, 1, 1, h2c) ++ { ++ @Override ++ protected SocketChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) ++ { ++ SocketChannelEndPoint endpoint = new SocketChannelEndPoint(channel, selectSet, key, getScheduler()) ++ { ++ @Override ++ public boolean flush(ByteBuffer... buffers) ++ { ++ // Fake TCP congestion. ++ return false; ++ } ++ ++ @Override ++ protected void onIncompleteFlush() ++ { ++ // Do nothing here to avoid spin loop, ++ // since the network is actually writable, ++ // as we are only faking TCP congestion. ++ } ++ }; ++ endpoint.setIdleTimeout(getIdleTimeout()); ++ return endpoint; ++ } ++ }; ++ connector.setIdleTimeout(idleTimeout); ++ server.addConnector(connector); ++ server.start(); ++ ++ prepareClient(); ++ httpClient.start(); ++ ++ InetSocketAddress address = new InetSocketAddress("localhost", connector.getLocalPort()); ++ // The connect() will complete exceptionally. ++ http2Client.connect(address, new Session.Listener() {}); ++ ++ await().atMost(Duration.ofMillis(5 * idleTimeout)).until(() -> connector.getConnectedEndPoints().size(), is(0)); ++ } ++ + private void sleep(long value) + { + try +diff --git a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java +index 8741a96846..c6b1b9ce50 100644 +--- a/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java ++++ b/jetty-core/jetty-http3/jetty-http3-common/src/main/java/org/eclipse/jetty/http3/HTTP3Session.java +@@ -218,8 +218,7 @@ public abstract class HTTP3Session extends ContainerLifeCycle implements Session + long error = HTTP3ErrorCode.REQUEST_CANCELLED_ERROR.code(); + String reason = "go_away"; + failStreams(stream -> true, error, reason, true, new ClosedChannelException()); +- terminate(); +- outwardDisconnect(error, reason); ++ terminateAndDisconnect(error, reason); + } + return CompletableFuture.completedFuture(null); + } +@@ -489,18 +488,12 @@ public abstract class HTTP3Session extends ContainerLifeCycle implements Session + goAwaySent = newGoAwayFrame(false); + GoAwayFrame goAwayFrame = goAwaySent; + zeroStreamsAction = () -> writeControlFrame(goAwayFrame, Callback.from(() -> +- { +- terminate(); +- outwardDisconnect(HTTP3ErrorCode.NO_ERROR.code(), "go_away"); +- })); ++ terminateAndDisconnect(HTTP3ErrorCode.NO_ERROR.code(), "go_away") ++ )); + } + else + { +- zeroStreamsAction = () -> +- { +- terminate(); +- outwardDisconnect(HTTP3ErrorCode.NO_ERROR.code(), "go_away"); +- }; ++ zeroStreamsAction = () -> terminateAndDisconnect(HTTP3ErrorCode.NO_ERROR.code(), "go_away"); + failStreams = true; + } + } +@@ -561,34 +554,24 @@ public abstract class HTTP3Session extends ContainerLifeCycle implements Session + public boolean onIdleTimeout() + { + boolean notify = false; ++ boolean terminate = false; + try (AutoLock ignored = lock.lock()) + { + switch (closeState) + { +- case NOT_CLOSED: +- { +- notify = true; +- break; +- } +- case LOCALLY_CLOSED: +- case REMOTELY_CLOSED: +- { +- break; +- } +- case CLOSING: +- case CLOSED: +- { +- if (LOG.isDebugEnabled()) +- LOG.debug("already closed, ignored idle timeout for {}", this); +- return false; +- } +- default: +- { +- throw new IllegalStateException(); +- } ++ case NOT_CLOSED -> notify = true; ++ case CLOSING, CLOSED -> terminate = true; + } + } + ++ if (terminate) ++ { ++ if (LOG.isDebugEnabled()) ++ LOG.debug("already closed, ignored idle timeout for {}", this); ++ terminateAndDisconnect(HTTP3ErrorCode.NO_ERROR.code(), "idle_timeout"); ++ return false; ++ } ++ + boolean confirmed = true; + if (notify) + confirmed = notifyIdleTimeout(); +@@ -645,18 +628,15 @@ public abstract class HTTP3Session extends ContainerLifeCycle implements Session + failStreams(stream -> true, error, reason, true, new IOException(reason)); + + if (goAwayFrame != null) +- { +- writeControlFrame(goAwayFrame, Callback.from(() -> +- { +- terminate(); +- outwardDisconnect(error, reason); +- })); +- } ++ writeControlFrame(goAwayFrame, Callback.from(() -> terminateAndDisconnect(error, reason))); + else +- { +- terminate(); +- outwardDisconnect(error, reason); +- } ++ terminateAndDisconnect(error, reason); ++ } ++ ++ private void terminateAndDisconnect(long error, String reason) ++ { ++ terminate(); ++ outwardDisconnect(error, reason); + } + + /** +diff --git a/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/IdleTimeoutTest.java b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/IdleTimeoutTest.java +new file mode 100644 +index 0000000000..3d450df6da +--- /dev/null ++++ b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/IdleTimeoutTest.java +@@ -0,0 +1,148 @@ ++// ++// ======================================================================== ++// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. ++// ++// This program and the accompanying materials are made available under the ++// terms of the Eclipse Public License v. 2.0 which is available at ++// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 ++// which is available at https://www.apache.org/licenses/LICENSE-2.0. ++// ++// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 ++// ======================================================================== ++// ++ ++package org.eclipse.jetty.http3.tests; ++ ++import java.io.IOException; ++import java.net.InetSocketAddress; ++import java.net.SocketAddress; ++import java.nio.ByteBuffer; ++import java.util.concurrent.CountDownLatch; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.atomic.AtomicBoolean; ++ ++import org.eclipse.jetty.http.HttpFields; ++import org.eclipse.jetty.http.HttpURI; ++import org.eclipse.jetty.http.HttpVersion; ++import org.eclipse.jetty.http.MetaData; ++import org.eclipse.jetty.http3.api.Session; ++import org.eclipse.jetty.http3.api.Stream; ++import org.eclipse.jetty.http3.client.HTTP3Client; ++import org.eclipse.jetty.http3.frames.HeadersFrame; ++import org.eclipse.jetty.http3.server.HTTP3ServerConnector; ++import org.eclipse.jetty.http3.server.RawHTTP3ServerConnectionFactory; ++import org.eclipse.jetty.io.EndPoint; ++import org.eclipse.jetty.quic.quiche.QuicheConnection; ++import org.eclipse.jetty.quic.server.ServerQuicConnection; ++import org.eclipse.jetty.quic.server.ServerQuicSession; ++import org.eclipse.jetty.server.HttpConfiguration; ++import org.eclipse.jetty.server.Server; ++import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; ++import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; ++import org.eclipse.jetty.util.component.LifeCycle; ++import org.eclipse.jetty.util.ssl.SslContextFactory; ++import org.eclipse.jetty.util.thread.QueuedThreadPool; ++import org.junit.jupiter.api.AfterEach; ++import org.junit.jupiter.api.BeforeEach; ++import org.junit.jupiter.api.Test; ++import org.junit.jupiter.api.extension.ExtendWith; ++ ++import static org.junit.jupiter.api.Assertions.assertTrue; ++ ++@ExtendWith(WorkDirExtension.class) ++public class IdleTimeoutTest ++{ ++ private Server server; ++ private HTTP3Client http3Client; ++ ++ @BeforeEach ++ public void prepare() ++ { ++ QueuedThreadPool serverExecutor = new QueuedThreadPool(); ++ serverExecutor.setName("server"); ++ server = new Server(); ++ } ++ ++ @AfterEach ++ public void dispose() ++ { ++ LifeCycle.stop(http3Client); ++ LifeCycle.stop(server); ++ } ++ ++ @Test ++ public void testIdleTimeoutWhenCongested(WorkDir workDir) throws Exception ++ { ++ long idleTimeout = 1000; ++ AtomicBoolean established = new AtomicBoolean(); ++ CountDownLatch disconnectLatch = new CountDownLatch(1); ++ RawHTTP3ServerConnectionFactory h3 = new RawHTTP3ServerConnectionFactory(new HttpConfiguration(), new Session.Server.Listener() ++ { ++ @Override ++ public void onAccept(Session session) ++ { ++ established.set(true); ++ } ++ ++ @Override ++ public void onDisconnect(Session session, long error, String reason) ++ { ++ disconnectLatch.countDown(); ++ } ++ }); ++ ++ CountDownLatch closeLatch = new CountDownLatch(1); ++ SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); ++ sslContextFactory.setKeyStorePath("src/test/resources/keystore.p12"); ++ sslContextFactory.setKeyStorePassword("storepwd"); ++ HTTP3ServerConnector connector = new HTTP3ServerConnector(server, sslContextFactory, h3) ++ { ++ @Override ++ protected ServerQuicConnection newConnection(EndPoint endpoint) ++ { ++ return new ServerQuicConnection(this, endpoint) ++ { ++ @Override ++ protected ServerQuicSession newQuicSession(SocketAddress remoteAddress, QuicheConnection quicheConnection) ++ { ++ return new ServerQuicSession(getExecutor(), getScheduler(), getByteBufferPool(), quicheConnection, this, remoteAddress, getQuicServerConnector()) ++ { ++ @Override ++ public int flush(long streamId, ByteBuffer buffer, boolean last) throws IOException ++ { ++ if (established.get()) ++ return 0; ++ return super.flush(streamId, buffer, last); ++ } ++ ++ @Override ++ public void outwardClose(long error, String reason) ++ { ++ closeLatch.countDown(); ++ super.outwardClose(error, reason); ++ } ++ }; ++ } ++ }; ++ } ++ }; ++ connector.getQuicConfiguration().setPemWorkDirectory(workDir.getEmptyPathDir()); ++ connector.setIdleTimeout(idleTimeout); ++ server.addConnector(connector); ++ server.start(); ++ ++ http3Client = new HTTP3Client(); ++ http3Client.getClientConnector().setSslContextFactory(new SslContextFactory.Client(true)); ++ http3Client.start(); ++ ++ Session.Client session = http3Client.connect(new InetSocketAddress("localhost", connector.getLocalPort()), new Session.Client.Listener() {}) ++ .get(5, TimeUnit.SECONDS); ++ ++ MetaData.Request request = new MetaData.Request("GET", HttpURI.from("http://localhost:" + connector.getLocalPort() + "/path"), HttpVersion.HTTP_3, HttpFields.EMPTY); ++ // The request will complete exceptionally. ++ session.newRequest(new HeadersFrame(request, true), new Stream.Client.Listener() {}); ++ ++ assertTrue(closeLatch.await(5 * idleTimeout, TimeUnit.MILLISECONDS)); ++ assertTrue(disconnectLatch.await(5 * idleTimeout, TimeUnit.MILLISECONDS)); ++ } ++} +diff --git a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java +index 15dc5d0eb6..606f444d9d 100644 +--- a/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java ++++ b/jetty-core/jetty-quic/jetty-quic-common/src/main/java/org/eclipse/jetty/quic/common/QuicSession.java +@@ -398,11 +398,15 @@ public abstract class QuicSession extends ContainerLifeCycle + + public void outwardClose(long error, String reason) + { ++ boolean closed = quicheConnection.close(error, reason); + if (LOG.isDebugEnabled()) +- LOG.debug("outward closing 0x{}/{} on {}", Long.toHexString(error), reason, this); +- quicheConnection.close(error, reason); +- // Flushing will eventually forward the outward close to the connection. +- flush(); ++ LOG.debug("outward closing ({}) 0x{}/{} on {}", closed, Long.toHexString(error), reason, this); ++ if (closed) ++ { ++ // Flushing will eventually forward ++ // the outward close to the connection. ++ flush(); ++ } + } + + private void finishOutwardClose(Throwable failure) +diff --git a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java +index d80c980685..a5726e261e 100644 +--- a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java ++++ b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/QuicServerConnector.java +@@ -305,6 +305,16 @@ public class QuicServerConnector extends AbstractNetworkConnector + throw new UnsupportedOperationException(getClass().getSimpleName() + " has no accept mechanism"); + } + ++ protected EndPoint newEndPoint(DatagramChannel channel, ManagedSelector selector, SelectionKey selectionKey) ++ { ++ return new DatagramChannelEndPoint(channel, selector, selectionKey, getScheduler()); ++ } ++ ++ protected ServerQuicConnection newConnection(EndPoint endpoint) ++ { ++ return new ServerQuicConnection(QuicServerConnector.this, endpoint); ++ } ++ + private class ServerDatagramSelectorManager extends SelectorManager + { + protected ServerDatagramSelectorManager(Executor executor, Scheduler scheduler, int selectors) +@@ -315,7 +325,7 @@ public class QuicServerConnector extends AbstractNetworkConnector + @Override + protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey) + { +- EndPoint endPoint = new DatagramChannelEndPoint((DatagramChannel)channel, selector, selectionKey, getScheduler()); ++ EndPoint endPoint = QuicServerConnector.this.newEndPoint((DatagramChannel)channel, selector, selectionKey); + endPoint.setIdleTimeout(getIdleTimeout()); + return endPoint; + } +@@ -323,7 +333,7 @@ public class QuicServerConnector extends AbstractNetworkConnector + @Override + public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) + { +- ServerQuicConnection connection = new ServerQuicConnection(QuicServerConnector.this, endpoint); ++ ServerQuicConnection connection = QuicServerConnector.this.newConnection(endpoint); + connection.addEventListener(container); + connection.setInputBufferSize(getInputBufferSize()); + connection.setOutputBufferSize(getOutputBufferSize()); +diff --git a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java +index 988a240531..08a212857f 100644 +--- a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java ++++ b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicConnection.java +@@ -45,13 +45,18 @@ public class ServerQuicConnection extends QuicConnection + private final QuicServerConnector connector; + private final SessionTimeouts sessionTimeouts; + +- protected ServerQuicConnection(QuicServerConnector connector, EndPoint endPoint) ++ public ServerQuicConnection(QuicServerConnector connector, EndPoint endPoint) + { + super(connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), endPoint); + this.connector = connector; + this.sessionTimeouts = new SessionTimeouts(connector.getScheduler()); + } + ++ public QuicServerConnector getQuicServerConnector() ++ { ++ return connector; ++ } ++ + @Override + public void onOpen() + { +@@ -87,13 +92,18 @@ public class ServerQuicConnection extends QuicConnection + } + else + { +- QuicSession session = new ServerQuicSession(getExecutor(), getScheduler(), bufferPool, quicheConnection, this, remoteAddress, connector); ++ ServerQuicSession session = newQuicSession(remoteAddress, quicheConnection); + // Send the response packet(s) that tryAccept() generated. + session.flush(); + return session; + } + } + ++ protected ServerQuicSession newQuicSession(SocketAddress remoteAddress, QuicheConnection quicheConnection) ++ { ++ return new ServerQuicSession(getExecutor(), getScheduler(), getByteBufferPool(), quicheConnection, this, remoteAddress, getQuicServerConnector()); ++ } ++ + public void schedule(ServerQuicSession session) + { + sessionTimeouts.schedule(session); +diff --git a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java +index 17598e557c..20e69736d2 100644 +--- a/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java ++++ b/jetty-core/jetty-quic/jetty-quic-server/src/main/java/org/eclipse/jetty/quic/server/ServerQuicSession.java +@@ -46,7 +46,7 @@ public class ServerQuicSession extends QuicSession implements CyclicTimeouts.Exp + private final Connector connector; + private long expireNanoTime = Long.MAX_VALUE; + +- protected ServerQuicSession(Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress, Connector connector) ++ public ServerQuicSession(Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, QuicheConnection quicheConnection, QuicConnection connection, SocketAddress remoteAddress, Connector connector) + { + super(executor, scheduler, bufferPool, quicheConnection, connection, remoteAddress); + this.connector = connector; +diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/IdleTimeoutTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/IdleTimeoutTest.java +new file mode 100644 +index 0000000000..0d1e13ea01 +--- /dev/null ++++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/IdleTimeoutTest.java +@@ -0,0 +1,103 @@ ++// ++// ======================================================================== ++// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. ++// ++// This program and the accompanying materials are made available under the ++// terms of the Eclipse Public License v. 2.0 which is available at ++// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 ++// which is available at https://www.apache.org/licenses/LICENSE-2.0. ++// ++// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 ++// ======================================================================== ++// ++ ++package org.eclipse.jetty.server; ++ ++import java.net.InetSocketAddress; ++import java.nio.ByteBuffer; ++import java.nio.channels.SelectionKey; ++import java.nio.channels.SocketChannel; ++import java.time.Duration; ++import java.util.concurrent.TimeUnit; ++ ++import org.eclipse.jetty.http.HttpTester; ++import org.eclipse.jetty.io.ManagedSelector; ++import org.eclipse.jetty.io.SocketChannelEndPoint; ++import org.eclipse.jetty.util.component.LifeCycle; ++import org.eclipse.jetty.util.thread.QueuedThreadPool; ++import org.junit.jupiter.api.AfterEach; ++import org.junit.jupiter.api.BeforeEach; ++import org.junit.jupiter.api.Test; ++ ++import static org.awaitility.Awaitility.await; ++import static org.hamcrest.Matchers.is; ++ ++public class IdleTimeoutTest ++{ ++ private Server server; ++ private ServerConnector connector; ++ ++ @BeforeEach ++ public void prepare() ++ { ++ QueuedThreadPool serverExecutor = new QueuedThreadPool(); ++ serverExecutor.setName("server"); ++ server = new Server(); ++ } ++ ++ @AfterEach ++ public void dispose() ++ { ++ LifeCycle.stop(server); ++ } ++ ++ @Test ++ public void testIdleTimeoutWhenCongested() throws Exception ++ { ++ long idleTimeout = 1000; ++ HttpConnectionFactory h1 = new HttpConnectionFactory(new HttpConfiguration()); ++ connector = new ServerConnector(server, 1, 1, h1) ++ { ++ @Override ++ protected SocketChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) ++ { ++ SocketChannelEndPoint endpoint = new SocketChannelEndPoint(channel, selectSet, key, getScheduler()) ++ { ++ @Override ++ public boolean flush(ByteBuffer... buffers) ++ { ++ // Fake TCP congestion. ++ return false; ++ } ++ ++ @Override ++ protected void onIncompleteFlush() ++ { ++ // Do nothing here to avoid spin loop, ++ // since the network is actually writable, ++ // as we are only faking TCP congestion. ++ } ++ }; ++ endpoint.setIdleTimeout(getIdleTimeout()); ++ return endpoint; ++ } ++ }; ++ connector.setIdleTimeout(idleTimeout); ++ server.addConnector(connector); ++ server.start(); ++ ++ try (SocketChannel client = SocketChannel.open()) ++ { ++ client.connect(new InetSocketAddress("localhost", connector.getLocalPort())); ++ ++ HttpTester.Request request = HttpTester.newRequest(); ++ client.write(request.generate()); ++ ++ // The server never writes back anything, but should close the connection. ++ client.configureBlocking(false); ++ ByteBuffer inputBuffer = ByteBuffer.allocate(1024); ++ await().atMost(Duration.ofSeconds(5)).until(() -> client.read(inputBuffer), is(-1)); ++ await().atMost(5, TimeUnit.SECONDS).until(() -> connector.getConnectedEndPoints().size(), is(0)); ++ } ++ } ++} +-- +2.28.0.windows.1 + diff --git a/jetty.spec b/jetty.spec index 6ab0e99b..73ed1389 100644 --- a/jetty.spec +++ b/jetty.spec @@ -1,4 +1,4 @@ -%define anolis_release 1 +%define anolis_release 2 %bcond_without bootstrap @@ -69,6 +69,8 @@ Source6: LICENSE-MIT Patch1: 0001-Distro-jetty.home.patch Patch2: 0002-Port-to-servlet-api-4-5.patch +#https://github.com/jetty/jetty.project/pull/11269/commits/59cd49c63de16481838ac0edfb05a4f9444365ee +Patch3: 0003-Fixes-11259-HTTP-2-connection-not-closed-after-idle-.patch %if %{with bootstrap} BuildRequires: javapackages-bootstrap @@ -980,6 +982,8 @@ exit 0 %license LICENSE NOTICE.txt LICENSE-MIT %changelog +* Fri Aug 09 2024 Kaiqiang Wang - 9.4.43-2 +- fix CVE-2024-22201 * Wed Jan 10 2024 mgb01105731 - 9.4.43-1 - update to version 9.4.43 -- Gitee