Skip to content

Commit 4c7f4fd

Browse files
committed
Requests with invalid content-length should always be rejected
1 parent 3e31c41 commit 4c7f4fd

File tree

3 files changed

+65
-13
lines changed

3 files changed

+65
-13
lines changed

java/org/apache/coyote/http11/Http11InputBuffer.java

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ private HeaderParseStatus parseHeader() throws IOException {
919919
headerData.lastSignificantChar = pos;
920920
byteBuffer.position(byteBuffer.position() - 1);
921921
// skipLine() will handle the error
922-
return skipLine();
922+
return skipLine(false);
923923
}
924924

925925
// chr is next byte of header name. Convert to lowercase.
@@ -930,7 +930,7 @@ private HeaderParseStatus parseHeader() throws IOException {
930930

931931
// Skip the line and ignore the header
932932
if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
933-
return skipLine();
933+
return skipLine(false);
934934
}
935935

936936
//
@@ -987,15 +987,11 @@ private HeaderParseStatus parseHeader() throws IOException {
987987
// CRLF or LF is an acceptable line terminator
988988
eol = true;
989989
} else if (prevChr == Constants.CR) {
990-
// Invalid value
991-
// Delete the header (it will be the most recent one)
992-
headers.removeHeader(headers.size() - 1);
993-
return skipLine();
990+
// Invalid value - also need to delete header
991+
return skipLine(true);
994992
} else if (chr != Constants.HT && HttpParser.isControl(chr)) {
995-
// Invalid value
996-
// Delete the header (it will be the most recent one)
997-
headers.removeHeader(headers.size() - 1);
998-
return skipLine();
993+
// Invalid value - also need to delete header
994+
return skipLine(true);
999995
} else if (chr == Constants.SP || chr == Constants.HT) {
1000996
byteBuffer.put(headerData.realPos, chr);
1001997
headerData.realPos++;
@@ -1043,7 +1039,27 @@ private HeaderParseStatus parseHeader() throws IOException {
10431039
}
10441040

10451041

1046-
private HeaderParseStatus skipLine() throws IOException {
1042+
private HeaderParseStatus skipLine(boolean deleteHeader) throws IOException {
1043+
boolean rejectThisHeader = rejectIllegalHeader;
1044+
// Check if rejectIllegalHeader is disabled and needs to be overridden
1045+
// for this header. The header name is required to determine if this
1046+
// override is required. The header name is only available once the
1047+
// header has been created. If the header has been created then
1048+
// deleteHeader will be true.
1049+
if (!rejectThisHeader && deleteHeader) {
1050+
if (headers.getName(headers.size() - 1).equalsIgnoreCase("content-length")) {
1051+
// Malformed content-length headers must always be rejected
1052+
// RFC 9112, section 6.3, bullet 5.
1053+
rejectThisHeader = true;
1054+
} else {
1055+
// Only need to delete the header if the request isn't going to
1056+
// be rejected (it will be the most recent one)
1057+
headers.removeHeader(headers.size() - 1);
1058+
}
1059+
}
1060+
1061+
// Parse the rest of the invalid header so we can construct a useful
1062+
// exception and/or debug message.
10471063
headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
10481064
boolean eol = false;
10491065

@@ -1069,11 +1085,11 @@ private HeaderParseStatus skipLine() throws IOException {
10691085
headerData.lastSignificantChar = pos;
10701086
}
10711087
}
1072-
if (rejectIllegalHeader || log.isDebugEnabled()) {
1088+
if (rejectThisHeader || log.isDebugEnabled()) {
10731089
String message = sm.getString("iib.invalidheader",
10741090
HeaderUtil.toPrintableString(byteBuffer.array(), headerData.lineStart,
10751091
headerData.lastSignificantChar - headerData.lineStart + 1));
1076-
if (rejectIllegalHeader) {
1092+
if (rejectThisHeader) {
10771093
throw new IllegalArgumentException(message);
10781094
}
10791095
log.debug(message);

test/org/apache/coyote/http11/TestHttp11InputBuffer.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,37 @@ public void testInvalidHeader01() {
709709
}
710710

711711

712+
@Test
713+
public void testInvalidContentLength01() {
714+
doTestInvalidContentLength(false);
715+
}
716+
717+
718+
@Test
719+
public void testInvalidContentLength02() {
720+
doTestInvalidContentLength(true);
721+
}
722+
723+
724+
private void doTestInvalidContentLength(boolean rejectIllegalHeader) {
725+
getTomcatInstance().getConnector().setProperty("rejectIllegalHeader", Boolean.toString(rejectIllegalHeader));
726+
727+
String[] request = new String[1];
728+
request[0] =
729+
"POST /test HTTP/1.1" + CRLF +
730+
"Host: localhost:8080" + CRLF +
731+
"Content-Length: 12\u000734" + CRLF +
732+
"Connection: close" + CRLF +
733+
CRLF;
734+
735+
InvalidClient client = new InvalidClient(request);
736+
737+
client.doRequest();
738+
Assert.assertTrue(client.getResponseLine(), client.isResponse400());
739+
Assert.assertTrue(client.isResponseBodyOK());
740+
}
741+
742+
712743
/**
713744
* Invalid request test client.
714745
*/

webapps/docs/changelog.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@
124124
<bug>66281</bug>: Fix unexpected timeouts that may appear as client
125125
disconnections when using HTTP/2 and NIO2. (markt)
126126
</fix>
127+
<fix>
128+
Enforce the requirement of RFC 7230 onwards that a request with a
129+
malformed <code>content-length</code> header should always be rejected
130+
with a 400 response. (markt)
131+
</fix>
127132
</changelog>
128133
</subsection>
129134
<subsection name="Jasper">

0 commit comments

Comments
 (0)