This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 7943c54211 Improve CRLF skipping for the available method
7943c54211 is described below
commit 7943c54211e96b31b93fbbd8ebb94f31961a3b10
Author: remm <[email protected]>
AuthorDate: Thu Jan 30 11:51:40 2025 +0100
Improve CRLF skipping for the available method
The bytes will now be consumed instead.
Also add processing for the chunk header since those are not actual
data.
Likely related to BZ69545 since the supposedly offending commit was
causing a testsuite failure following the TestChunkedInputFilter update.
---
.../coyote/http11/filters/ChunkedInputFilter.java | 121 ++++++++++++++++++++-
webapps/docs/changelog.xml | 4 +
2 files changed, 122 insertions(+), 3 deletions(-)
diff --git a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
index debdf57ed8..4621ab82e4 100644
--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -199,13 +199,32 @@ public class ChunkedInputFilter implements InputFilter,
ApplicationBufferHandler
available = readChunk.remaining();
}
- // Handle some edge cases
+ if (available > 2 && (parseState == ParseState.CHUNK_BODY_CRLF ||
parseState == ParseState.CHUNK_HEADER)) {
+ if (parseState == ParseState.CHUNK_BODY_CRLF) {
+ skipCRLF();
+ }
+ if (parseState == ParseState.CHUNK_HEADER) {
+ skipChunkHeader();
+ }
+ available = readChunk.remaining();
+ // If ending as TRAILER_FIELDS, then the next read will be EOF and
available can be > 0
+ // If ending as CHUNK_HEADER then there's nothing left to read for
now
+ // If ending as CHUNK_BODY_CRLF, then if there's more than two
left, there will be data to read or leading to EOF
+ }
if (available == 1 && parseState == ParseState.CHUNK_BODY_CRLF) {
// Either just the CR or just the LF are left in the buffer. There
is no data to read.
- available = 0;
+ if (!skipCRLF()) {
+ available = readChunk.remaining();
+ } else {
+ available = 0;
+ }
} else if (available == 2 && !crFound && parseState ==
ParseState.CHUNK_BODY_CRLF) {
// Just CRLF is left in the buffer. There is no data to read.
- available = 0;
+ if (!skipCRLF()) {
+ available = readChunk.remaining();
+ } else {
+ available = 0;
+ }
}
if (available == 0) {
@@ -391,6 +410,69 @@ public class ChunkedInputFilter implements InputFilter,
ApplicationBufferHandler
}
+ private boolean skipChunkHeader() {
+
+ boolean eol = false;
+
+ while (!eol) {
+ if (readChunk == null || readChunk.position() >=
readChunk.limit()) {
+ return false;
+ }
+
+ byte chr = readChunk.get(readChunk.position());
+ if (chr == Constants.CR || chr == Constants.LF) {
+ parsingExtension = false;
+ if (!skipCRLF()) {
+ return false;
+ }
+ eol = true;
+ } else if (chr == Constants.SEMI_COLON && !parsingExtension) {
+ // First semi-colon marks the start of the extension. Further
+ // semi-colons may appear to separate multiple
chunk-extensions.
+ // These need to be processed as part of parsing the
extensions.
+ parsingExtension = true;
+ extensionSize++;
+ } else if (!parsingExtension) {
+ int charValue = HexUtils.getDec(chr);
+ if (charValue != -1 && chunkSizeDigitsRead < 8) {
+ chunkSizeDigitsRead++;
+ remaining = (remaining << 4) | charValue;
+ } else {
+ // Isn't valid hex so this is an error condition
+ return false;
+ }
+ } else {
+ // Extension 'parsing'
+ // Note that the chunk-extension is neither parsed nor
+ // validated. Currently it is simply ignored.
+ extensionSize++;
+ if (maxExtensionSize > -1 && extensionSize > maxExtensionSize)
{
+ return false;
+ }
+ }
+
+ // Parsing the CRLF increments pos
+ if (!eol) {
+ readChunk.position(readChunk.position() + 1);
+ }
+ }
+
+ if (chunkSizeDigitsRead == 0 || remaining < 0) {
+ return false;
+ } else {
+ chunkSizeDigitsRead = 0;
+ }
+
+ if (remaining == 0) {
+ parseState = ParseState.TRAILER_FIELDS;
+ } else {
+ parseState = ParseState.CHUNK_BODY;
+ }
+
+ return true;
+ }
+
+
private int parseChunkBody(ApplicationBufferHandler handler) throws
IOException {
int result = 0;
@@ -461,6 +543,39 @@ public class ChunkedInputFilter implements InputFilter,
ApplicationBufferHandler
}
+ private boolean skipCRLF() {
+
+ boolean eol = false;
+
+ while (!eol) {
+ if (readChunk == null || readChunk.position() >=
readChunk.limit()) {
+ return false;
+ }
+
+ byte chr = readChunk.get(readChunk.position());
+ if (chr == Constants.CR) {
+ if (crFound) {
+ return false;
+ }
+ crFound = true;
+ } else if (chr == Constants.LF) {
+ if (!crFound) {
+ return false;
+ }
+ eol = true;
+ } else {
+ return false;
+ }
+
+ readChunk.position(readChunk.position() + 1);
+ }
+
+ crFound = false;
+ parseState = ParseState.CHUNK_HEADER;
+ return true;
+ }
+
+
/**
* Parse end chunk data.
*
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 6cefa4444f..1b2a09b0bc 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -262,6 +262,10 @@
<strong>allowBackslash</strong> attributes determines how the decoded
URI is processed. (markt)
</add>
+ <fix>
+ <bug>69545</bug>: Improve CRLF skipping for the <code>available</code>
+ method of the <code>ChunkedInputFilter</code>. (remm)
+ </fix>
</changelog>
</subsection>
<subsection name="Jasper">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]