Skip to content

Commit ed4266f

Browse files
authored
Merge pull request #1612 from fl4via/2.2.x_backport-bug-fixes
[UNDERTOW-2405][UNDERTOW-2391][UNDERTOW-2407][UNDERTOW-2408][UNDERTOW-2334] CVE-2024-27316 CVE-2023-5685 CVE-2024-6162 Backport bug fixes to 2.2.x
2 parents 303ccdf + a28ac53 commit ed4266f

14 files changed

+364
-39
lines changed

core/pom.xml

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,9 @@
413413
<configuration>
414414
<enableAssertions>true</enableAssertions>
415415
<runOrder>reversealphabetical</runOrder>
416+
<excludes>
417+
<exclude>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</exclude>
418+
</excludes>
416419
<systemPropertyVariables>
417420
<test.h2>true</test.h2>
418421
<test.dump>${dump}</test.dump>
@@ -437,6 +440,9 @@
437440
<configuration>
438441
<enableAssertions>true</enableAssertions>
439442
<runOrder>reversealphabetical</runOrder>
443+
<excludes>
444+
<exclude>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</exclude>
445+
</excludes>
440446
<systemPropertyVariables>
441447
<test.h2c>true</test.h2c>
442448
<test.dump>${dump}</test.dump>
@@ -452,7 +458,6 @@
452458
<reportsDirectory>${project.build.directory}/surefire-h2c-reports</reportsDirectory>
453459
</configuration>
454460
</execution>
455-
456461
<execution>
457462
<id>proxy-h2c-upgrade</id>
458463
<phase>test</phase>
@@ -462,6 +467,93 @@
462467
<configuration>
463468
<enableAssertions>true</enableAssertions>
464469
<runOrder>reversealphabetical</runOrder>
470+
<excludes>
471+
<exclude>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</exclude>
472+
</excludes>
473+
<systemPropertyVariables>
474+
<test.h2c-upgrade>true</test.h2c-upgrade>
475+
<test.dump>${dump}</test.dump>
476+
<test.bufferSize>${bufferSize}</test.bufferSize>
477+
<default.server.address>localhost</default.server.address>
478+
<default.server.port>7777</default.server.port>
479+
<java.util.logging.manager>org.jboss.logmanager.LogManager
480+
</java.util.logging.manager>
481+
<test.level>${test.level}</test.level>
482+
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
483+
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
484+
</systemPropertyVariables>
485+
<reportsDirectory>${project.build.directory}/surefire-h2c-upgrade-reports</reportsDirectory>
486+
</configuration>
487+
</execution>
488+
<!-- TODO: remove these executions as soon as UndertowOptions.MAX_HTTP2_HEADER_SIZE is created -->
489+
<execution>
490+
<id>proxy-h2-big-http2-max-header-size</id>
491+
<phase>test</phase>
492+
<goals>
493+
<goal>test</goal>
494+
</goals>
495+
<configuration>
496+
<enableAssertions>true</enableAssertions>
497+
<runOrder>reversealphabetical</runOrder>
498+
<includes>
499+
<include>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</include>
500+
</includes>
501+
<systemPropertyVariables>
502+
<test.h2>true</test.h2>
503+
<test.dump>${dump}</test.dump>
504+
<test.bufferSize>${bufferSize}</test.bufferSize>
505+
<default.server.address>localhost</default.server.address>
506+
<default.server.port>7777</default.server.port>
507+
<java.util.logging.manager>org.jboss.logmanager.LogManager
508+
</java.util.logging.manager>
509+
<test.level>${test.level}</test.level>
510+
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
511+
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
512+
<io.undertow.http2-max-header-size>600000</io.undertow.http2-max-header-size>
513+
</systemPropertyVariables>
514+
<reportsDirectory>${project.build.directory}/surefire-h2-reports</reportsDirectory>
515+
</configuration>
516+
</execution>
517+
<execution>
518+
<id>proxy-h2c-big-http2-max-header-size</id>
519+
<phase>test</phase>
520+
<goals>
521+
<goal>test</goal>
522+
</goals>
523+
<configuration>
524+
<enableAssertions>true</enableAssertions>
525+
<runOrder>reversealphabetical</runOrder>
526+
<includes>
527+
<include>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</include>
528+
</includes>
529+
<systemPropertyVariables>
530+
<test.h2c>true</test.h2c>
531+
<test.dump>${dump}</test.dump>
532+
<test.bufferSize>${bufferSize}</test.bufferSize>
533+
<default.server.address>localhost</default.server.address>
534+
<default.server.port>7777</default.server.port>
535+
<java.util.logging.manager>org.jboss.logmanager.LogManager
536+
</java.util.logging.manager>
537+
<test.level>${test.level}</test.level>
538+
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
539+
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
540+
<io.undertow.http2-max-header-size>600000</io.undertow.http2-max-header-size>
541+
</systemPropertyVariables>
542+
<reportsDirectory>${project.build.directory}/surefire-h2c-reports</reportsDirectory>
543+
</configuration>
544+
</execution>
545+
<execution>
546+
<id>proxy-h2c-upgrade-big-http2-max-header-size</id>
547+
<phase>test</phase>
548+
<goals>
549+
<goal>test</goal>
550+
</goals>
551+
<configuration>
552+
<enableAssertions>true</enableAssertions>
553+
<runOrder>reversealphabetical</runOrder>
554+
<includes>
555+
<include>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</include>
556+
</includes>
465557
<systemPropertyVariables>
466558
<test.h2c-upgrade>true</test.h2c-upgrade>
467559
<test.dump>${dump}</test.dump>
@@ -473,6 +565,7 @@
473565
<test.level>${test.level}</test.level>
474566
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
475567
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
568+
<io.undertow.http2-max-header-size>600000</io.undertow.http2-max-header-size>
476569
</systemPropertyVariables>
477570
<reportsDirectory>${project.build.directory}/surefire-h2c-upgrade-reports</reportsDirectory>
478571
</configuration>

core/src/main/java/io/undertow/UndertowMessages.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,8 @@ public interface UndertowMessages {
518518
// @Message(id = 159, value = "Max size must be larger than one")
519519
// IllegalArgumentException maxSizeMustBeLargerThanOne();
520520

521-
@Message(id = 161, value = "HTTP/2 header block is too large")
522-
String headerBlockTooLarge();
521+
@Message(id = 161, value = "HTTP/2 header block is too large, maximum header size is %s")
522+
String headerBlockTooLarge(int maxHeaderSize);
523523

524524
@Message(id = 162, value = "An invalid SameSite attribute [%s] is specified. It must be one of %s")
525525
IllegalArgumentException invalidSameSiteMode(String mode, String validAttributes);

core/src/main/java/io/undertow/protocols/http2/Http2Channel.java

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@
7070
*/
7171
public class Http2Channel extends AbstractFramedChannel<Http2Channel, AbstractHttp2StreamSourceChannel, AbstractHttp2StreamSinkChannel> implements Attachable {
7272

73+
// HTTP2 MAX HEADER SIZE constants, to be properly replaced by UndertowOptions
74+
/**
75+
* Name of the system property for configuring HTTP2 max header size: {@code "io.undertow.http2-max-header-size"}.
76+
* <p>This system property will be replaced by an {@link UndertowOptions Undertow option}.</p>
77+
*/
78+
public static final String HTTP2_MAX_HEADER_SIZE_PROPERTY = "io.undertow.http2-max-header-size";
79+
/**
80+
* Default value of HTTP2 max header size. If the system property {@code "io.undertow.http2-max-header-size"} is left
81+
* undefined, this value will be used for the corresponding {@link #HTTP2_MAX_HEADER_SIZE system property configuration}.
82+
*/
83+
private static final int DEFAULT_HTTP2_MAX_HEADER_SIZE = 20000;
84+
/**
85+
* System property {@code "io.undertow.http2-max-header-size"} for configuring the max header size configuration for HTTP2
86+
* messages.
87+
* <p>
88+
* The effective maximum size for headers to be used by this channel will be the minimum of the three: this system property,
89+
* {@link UndertowOptions#MAX_HEADER_SIZE}, and {@link UndertowOptions#HTTP2_SETTINGS_HEADER_TABLE_SIZE}. When calculating
90+
* the minimum, only positive values are taken into account, as values of -1 indicate the absence of a limit.
91+
* <p>
92+
* To be replaced by an {@link UndertowOptions Undertow option} in a future release.
93+
*/
94+
private static final int HTTP2_MAX_HEADER_SIZE = Integer.getInteger(HTTP2_MAX_HEADER_SIZE_PROPERTY, DEFAULT_HTTP2_MAX_HEADER_SIZE);
95+
7396
public static final String CLEARTEXT_UPGRADE_STRING = "h2c";
7497

7598

@@ -240,7 +263,7 @@ public Http2Channel(StreamConnection connectedStreamChannel, String protocol, By
240263
encoderHeaderTableSize = settings.get(UndertowOptions.HTTP2_SETTINGS_HEADER_TABLE_SIZE, Hpack.DEFAULT_TABLE_SIZE);
241264
receiveMaxFrameSize = settings.get(UndertowOptions.HTTP2_SETTINGS_MAX_FRAME_SIZE, DEFAULT_MAX_FRAME_SIZE);
242265
maxPadding = settings.get(UndertowOptions.HTTP2_PADDING_SIZE, 0);
243-
maxHeaderListSize = settings.get(UndertowOptions.HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, settings.get(UndertowOptions.MAX_HEADER_SIZE, -1));
266+
maxHeaderListSize = getMaxHeaderSize(settings);
244267
if(maxPadding > 0) {
245268
paddingRandom = new SecureRandom();
246269
} else {
@@ -311,6 +334,42 @@ public void handleEvent(Http2Channel channel) {
311334
}
312335
}
313336

337+
private static int getMaxHeaderSize(OptionMap settings) {
338+
final int maxHeaderSizeConfig = settings.get(UndertowOptions.MAX_HEADER_SIZE, HTTP2_MAX_HEADER_SIZE);
339+
// soon to be removed http2 settings max header
340+
final int http2SettingsMaxHeaderListSize = settings.get(UndertowOptions.HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, HTTP2_MAX_HEADER_SIZE);
341+
if (HTTP2_MAX_HEADER_SIZE > 0) {
342+
if (maxHeaderSizeConfig > 0) {
343+
if (http2SettingsMaxHeaderListSize > 0) {
344+
return Math.min(Math.min(HTTP2_MAX_HEADER_SIZE, maxHeaderSizeConfig), http2SettingsMaxHeaderListSize);
345+
} else {
346+
return Math.min(HTTP2_MAX_HEADER_SIZE, maxHeaderSizeConfig);
347+
}
348+
} else {
349+
if (http2SettingsMaxHeaderListSize > 0) {
350+
return Math.min(HTTP2_MAX_HEADER_SIZE, http2SettingsMaxHeaderListSize);
351+
} else {
352+
return HTTP2_MAX_HEADER_SIZE;
353+
}
354+
}
355+
} else {
356+
if (maxHeaderSizeConfig > 0) {
357+
if (http2SettingsMaxHeaderListSize > 0) {
358+
return Math.min(maxHeaderSizeConfig, http2SettingsMaxHeaderListSize);
359+
} else {
360+
return maxHeaderSizeConfig;
361+
}
362+
} else {
363+
if (http2SettingsMaxHeaderListSize > 0) {
364+
return http2SettingsMaxHeaderListSize;
365+
} else {
366+
// replace any value <= 0 by -1
367+
return -1;
368+
}
369+
}
370+
}
371+
}
372+
314373
private void sendSettings() {
315374
List<Http2Setting> settings = new ArrayList<>();
316375
settings.add(new Http2Setting(Http2Setting.SETTINGS_HEADER_TABLE_SIZE, encoderHeaderTableSize));
@@ -690,7 +749,10 @@ protected Collection<AbstractFramedStreamSourceChannel<Http2Channel, AbstractHtt
690749
List<AbstractFramedStreamSourceChannel<Http2Channel, AbstractHttp2StreamSourceChannel, AbstractHttp2StreamSinkChannel>> channels = new ArrayList<>(currentStreams.size());
691750
for(Map.Entry<Integer, StreamHolder> entry : currentStreams.entrySet()) {
692751
if(!entry.getValue().sourceClosed) {
693-
channels.add(entry.getValue().sourceChannel);
752+
final Http2StreamSourceChannel sourceChannel = entry.getValue().sourceChannel;
753+
if (sourceChannel != null) {
754+
channels.add(sourceChannel);
755+
}
694756
}
695757
}
696758
return channels;

core/src/main/java/io/undertow/protocols/http2/Http2FrameHeaderParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ public boolean handle(final ByteBuffer byteBuffer) throws IOException {
108108
throw UndertowMessages.MESSAGES.http2ContinuationFrameNotExpected();
109109
}
110110
parser = continuationParser;
111-
continuationParser.moreData(length);
111+
if (!continuationParser.moreData(length)) {
112+
http2Channel.sendGoAway(Http2Channel.ERROR_PROTOCOL_ERROR);
113+
}
112114
break;
113115
}
114116
case FRAME_TYPE_PUSH_PROMISE: {

core/src/main/java/io/undertow/protocols/http2/Http2HeaderBlockParser.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ abstract class Http2HeaderBlockParser extends Http2PushBackParser implements Hpa
4646

4747
private final HpackDecoder decoder;
4848
private int frameRemaining = -1;
49+
private int totalHeaderLength = 0;
4950
private boolean invalid = false;
5051
private boolean processingPseudoHeaders = true;
5152
private final boolean client;
@@ -81,7 +82,7 @@ abstract class Http2HeaderBlockParser extends Http2PushBackParser implements Hpa
8182
protected void handleData(ByteBuffer resource, Http2FrameHeaderParser header) throws IOException {
8283
boolean continuationFramesComing = Bits.anyAreClear(header.flags, Http2Channel.HEADERS_FLAG_END_HEADERS);
8384
if (frameRemaining == -1) {
84-
frameRemaining = header.length;
85+
frameRemaining = totalHeaderLength = header.length;
8586
}
8687
final boolean moreDataThisFrame = resource.remaining() < frameRemaining;
8788
final int pos = resource.position();
@@ -156,7 +157,7 @@ public void emitHeader(HttpString name, String value, boolean neverIndex) throws
156157
if(maxHeaderListSize > 0) {
157158
headerSize += (name.length() + value.length() + 32);
158159
if (headerSize > maxHeaderListSize) {
159-
throw new HpackException(UndertowMessages.MESSAGES.headerBlockTooLarge(), Http2Channel.ERROR_PROTOCOL_ERROR);
160+
throw new HpackException(UndertowMessages.MESSAGES.headerBlockTooLarge(maxHeaderListSize), Http2Channel.ERROR_PROTOCOL_ERROR);
160161
}
161162
}
162163
if(maxHeaders > 0 && headerMap.size() > maxHeaders) {
@@ -200,9 +201,15 @@ public void emitHeader(HttpString name, String value, boolean neverIndex) throws
200201
}
201202
protected abstract int getPaddingLength();
202203
@Override
203-
protected void moreData(int data) {
204-
super.moreData(data);
204+
protected boolean moreData(int data) {
205+
boolean acceptMoreData = super.moreData(data);
205206
frameRemaining += data;
207+
totalHeaderLength += data;
208+
if (maxHeaderListSize > 0 && totalHeaderLength > maxHeaderListSize) {
209+
UndertowLogger.REQUEST_LOGGER.debug(UndertowMessages.MESSAGES.headerBlockTooLarge(maxHeaderListSize));
210+
return false;
211+
}
212+
return acceptMoreData;
206213
}
207214

208215
public boolean isInvalid() {

core/src/main/java/io/undertow/protocols/http2/Http2PushBackParser.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818

1919
package io.undertow.protocols.http2;
2020

21+
import io.undertow.UndertowMessages;
22+
2123
import java.io.IOException;
2224
import java.nio.ByteBuffer;
2325

24-
import io.undertow.UndertowMessages;
25-
2626
/**
2727
* Parser that supports push back when not all data can be read.
2828
*
@@ -123,9 +123,10 @@ protected void finish() {
123123
finished = true;
124124
}
125125

126-
protected void moreData(int data) {
126+
protected boolean moreData(int data) {
127127
finished = false;
128128
this.remainingData += data;
129+
return true;
129130
}
130131

131132
public int getFrameLength() {

0 commit comments

Comments
 (0)