Skip to content

Commit 296636d

Browse files
committed
[UNDERTOW-2405] CVE-2024-27316 Create system property io.undertow.http2-max-header-size, for configuring the maximum size of HTTP2 header sizes, default value set to 20000
Add a test for that new configuration and update affected tests accordingly. Also: add TODO place holders for new config Undertow.HTTP2_MAX_HEADER_SIZE to be added in Undertow 2.4.0.Final. Signed-off-by: Flavia Rainone <frainone@redhat.com>
1 parent d798de6 commit 296636d

9 files changed

+340
-19
lines changed

core/pom.xml

+94-1
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@
372372
<configuration>
373373
<enableAssertions>true</enableAssertions>
374374
<runOrder>reversealphabetical</runOrder>
375+
<excludes>
376+
<exclude>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</exclude>
377+
</excludes>
375378
<systemPropertyVariables>
376379
<test.h2>true</test.h2>
377380
<test.dump>${dump}</test.dump>
@@ -396,6 +399,9 @@
396399
<configuration>
397400
<enableAssertions>true</enableAssertions>
398401
<runOrder>reversealphabetical</runOrder>
402+
<excludes>
403+
<exclude>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</exclude>
404+
</excludes>
399405
<systemPropertyVariables>
400406
<test.h2c>true</test.h2c>
401407
<test.dump>${dump}</test.dump>
@@ -411,7 +417,6 @@
411417
<reportsDirectory>${project.build.directory}/surefire-h2c-reports</reportsDirectory>
412418
</configuration>
413419
</execution>
414-
415420
<execution>
416421
<id>proxy-h2c-upgrade</id>
417422
<phase>test</phase>
@@ -421,6 +426,93 @@
421426
<configuration>
422427
<enableAssertions>true</enableAssertions>
423428
<runOrder>reversealphabetical</runOrder>
429+
<excludes>
430+
<exclude>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</exclude>
431+
</excludes>
432+
<systemPropertyVariables>
433+
<test.h2c-upgrade>true</test.h2c-upgrade>
434+
<test.dump>${dump}</test.dump>
435+
<test.bufferSize>${bufferSize}</test.bufferSize>
436+
<default.server.address>localhost</default.server.address>
437+
<default.server.port>7777</default.server.port>
438+
<java.util.logging.manager>org.jboss.logmanager.LogManager
439+
</java.util.logging.manager>
440+
<test.level>${test.level}</test.level>
441+
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
442+
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
443+
</systemPropertyVariables>
444+
<reportsDirectory>${project.build.directory}/surefire-h2c-upgrade-reports</reportsDirectory>
445+
</configuration>
446+
</execution>
447+
<!-- TODO: remove these executions as soon as UndertowOptions.MAX_HTTP2_HEADER_SIZE is created -->
448+
<execution>
449+
<id>proxy-h2-big-http2-max-header-size</id>
450+
<phase>test</phase>
451+
<goals>
452+
<goal>test</goal>
453+
</goals>
454+
<configuration>
455+
<enableAssertions>true</enableAssertions>
456+
<runOrder>reversealphabetical</runOrder>
457+
<includes>
458+
<include>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</include>
459+
</includes>
460+
<systemPropertyVariables>
461+
<test.h2>true</test.h2>
462+
<test.dump>${dump}</test.dump>
463+
<test.bufferSize>${bufferSize}</test.bufferSize>
464+
<default.server.address>localhost</default.server.address>
465+
<default.server.port>7777</default.server.port>
466+
<java.util.logging.manager>org.jboss.logmanager.LogManager
467+
</java.util.logging.manager>
468+
<test.level>${test.level}</test.level>
469+
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
470+
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
471+
<io.undertow.http2-max-header-size>600000</io.undertow.http2-max-header-size>
472+
</systemPropertyVariables>
473+
<reportsDirectory>${project.build.directory}/surefire-h2-reports</reportsDirectory>
474+
</configuration>
475+
</execution>
476+
<execution>
477+
<id>proxy-h2c-big-http2-max-header-size</id>
478+
<phase>test</phase>
479+
<goals>
480+
<goal>test</goal>
481+
</goals>
482+
<configuration>
483+
<enableAssertions>true</enableAssertions>
484+
<runOrder>reversealphabetical</runOrder>
485+
<includes>
486+
<include>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</include>
487+
</includes>
488+
<systemPropertyVariables>
489+
<test.h2c>true</test.h2c>
490+
<test.dump>${dump}</test.dump>
491+
<test.bufferSize>${bufferSize}</test.bufferSize>
492+
<default.server.address>localhost</default.server.address>
493+
<default.server.port>7777</default.server.port>
494+
<java.util.logging.manager>org.jboss.logmanager.LogManager
495+
</java.util.logging.manager>
496+
<test.level>${test.level}</test.level>
497+
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
498+
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
499+
<io.undertow.http2-max-header-size>600000</io.undertow.http2-max-header-size>
500+
</systemPropertyVariables>
501+
<reportsDirectory>${project.build.directory}/surefire-h2c-reports</reportsDirectory>
502+
</configuration>
503+
</execution>
504+
<execution>
505+
<id>proxy-h2c-upgrade-big-http2-max-header-size</id>
506+
<phase>test</phase>
507+
<goals>
508+
<goal>test</goal>
509+
</goals>
510+
<configuration>
511+
<enableAssertions>true</enableAssertions>
512+
<runOrder>reversealphabetical</runOrder>
513+
<includes>
514+
<include>*LongURLTestCase, *LotsOfQueryParametersTestCase, *LotsOfHeadersResponseTestCase</include>
515+
</includes>
424516
<systemPropertyVariables>
425517
<test.h2c-upgrade>true</test.h2c-upgrade>
426518
<test.dump>${dump}</test.dump>
@@ -432,6 +524,7 @@
432524
<test.level>${test.level}</test.level>
433525
<java.net.preferIPv6Addresses>${test.ipv6}</java.net.preferIPv6Addresses>
434526
<sun.net.useExclusiveBind>false</sun.net.useExclusiveBind>
527+
<io.undertow.http2-max-header-size>600000</io.undertow.http2-max-header-size>
435528
</systemPropertyVariables>
436529
<reportsDirectory>${project.build.directory}/surefire-h2c-upgrade-reports</reportsDirectory>
437530
</configuration>

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

+2-2
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

+64-2
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, UndertowOptions.DEFAULT_MAX_HEADER_SIZE));
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/Http2HeaderBlockParser.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ public void emitHeader(HttpString name, String value, boolean neverIndex) throws
157157
if(maxHeaderListSize > 0) {
158158
headerSize += (name.length() + value.length() + 32);
159159
if (headerSize > maxHeaderListSize) {
160-
throw new HpackException(UndertowMessages.MESSAGES.headerBlockTooLarge(), Http2Channel.ERROR_PROTOCOL_ERROR);
160+
throw new HpackException(UndertowMessages.MESSAGES.headerBlockTooLarge(maxHeaderListSize), Http2Channel.ERROR_PROTOCOL_ERROR);
161161
}
162162
}
163163
if(maxHeaders > 0 && headerMap.size() > maxHeaders) {
@@ -205,7 +205,11 @@ protected boolean moreData(int data) {
205205
boolean acceptMoreData = super.moreData(data);
206206
frameRemaining += data;
207207
totalHeaderLength += data;
208-
return acceptMoreData && totalHeaderLength < maxHeaderListSize;
208+
if (maxHeaderListSize > 0 && totalHeaderLength > maxHeaderListSize) {
209+
UndertowLogger.REQUEST_LOGGER.debug(UndertowMessages.MESSAGES.headerBlockTooLarge(maxHeaderListSize));
210+
return false;
211+
}
212+
return acceptMoreData;
209213
}
210214

211215
public boolean isInvalid() {

0 commit comments

Comments
 (0)