This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 9.2.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/9.2.x by this push:
     new c9206d7315 Fix a bug in parsing chunked messages (#11073)
c9206d7315 is described below

commit c9206d7315c400edbc5a6be3a10e54384266bcf1
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Wed Feb 28 09:41:44 2024 -0700

    Fix a bug in parsing chunked messages (#11073)
    
    * Fix a bug in parsing chunked messages
    
    * Add tests
    
    * Fix another case
    
    * Allow LF
    
    (cherry picked from commit 47e23d7d278a0562bcf27c170a32e5241a04a9e5)
---
 proxy/http/HttpTunnel.cc                           | 13 ++++--
 .../chunked_encoding/bad_chunked_encoding.test.py  | 17 +++++---
 .../replays/malformed_chunked_header.replay.yaml   | 49 ++++++++++++++++++++--
 3 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/proxy/http/HttpTunnel.cc b/proxy/http/HttpTunnel.cc
index dadd6c8ce0..25beb78e4c 100644
--- a/proxy/http/HttpTunnel.cc
+++ b/proxy/http/HttpTunnel.cc
@@ -153,8 +153,9 @@ ChunkedHandler::read_size()
           }
         } else {
           // We are done parsing size
-          if (num_digits == 0 || running_sum < 0) {
-            // Bogus chunk size
+          if ((num_digits == 0 || running_sum < 0) ||       /* Bogus chunk 
size */
+              (!ParseRules::is_wslfcr(*tmp) && *tmp != ';') /* Unexpected 
character */
+          ) {
             state = CHUNK_READ_ERROR;
             done  = true;
             break;
@@ -171,10 +172,16 @@ ChunkedHandler::read_size()
           break;
         }
       } else if (state == CHUNK_READ_SIZE_START) {
-        if (ParseRules::is_lf(*tmp)) {
+        if (ParseRules::is_cr(*tmp)) {
+          // Skip it
+        } else if (ParseRules::is_lf(*tmp) &&
+                   bytes_used <= 2) { // bytes_used should be 2 if it's CRLF, 
but permit a single LF as well
           running_sum = 0;
           num_digits  = 0;
           state       = CHUNK_READ_SIZE;
+        } else { // Unexpected character
+          state = CHUNK_READ_ERROR;
+          done  = true;
         }
       }
       tmp++;
diff --git a/tests/gold_tests/chunked_encoding/bad_chunked_encoding.test.py 
b/tests/gold_tests/chunked_encoding/bad_chunked_encoding.test.py
index 3b163259ce..e92181ccdf 100644
--- a/tests/gold_tests/chunked_encoding/bad_chunked_encoding.test.py
+++ b/tests/gold_tests/chunked_encoding/bad_chunked_encoding.test.py
@@ -125,13 +125,15 @@ class MalformedChunkHeaderTest:
     def setupOriginServer(self):
         self.server = Test.MakeVerifierServerProcess("verifier-server2", 
self.chunkedReplayFile)
 
-        # The server's responses will fail the first two transactions
+        # The server's responses will fail the first three transactions
         # because ATS will close the connection due to the malformed
         # chunk headers.
         self.server.Streams.stdout += Testers.ContainsExpression(
             "Unexpected chunked content for key 1: too small", "Verify that 
writing the first response failed.")
+        self.server.Streams.stdout += Testers.ExcludesExpression(
+            "chunked body of 3 bytes for key 2 with chunk stream", "Verify 
that writing the second response failed.")
         self.server.Streams.stdout += Testers.ContainsExpression(
-            "Unexpected chunked content for key 2: too small", "Verify that 
writing the second response failed.")
+            "Unexpected chunked content for key 3: too small", "Verify that 
writing the third response failed.")
 
         # ATS should close the connection before any body gets through. "abc"
         # is the body sent by the client for each of these chunked cases.
@@ -169,12 +171,15 @@ class MalformedChunkHeaderTest:
         # The aborted connections will result in errors and a non-zero return
         # code from the verifier client.
         tr.Processes.Default.ReturnCode = 1
-        tr.Processes.Default.Streams.stdout += Testers.ContainsExpression(
-            r"(Unexpected chunked content for key 3: too small|Failed HTTP/1 
transaction with key: 3)",
-            "Verify that ATS closed the third transaction.")
         tr.Processes.Default.Streams.stdout += Testers.ContainsExpression(
             r"(Unexpected chunked content for key 4: too small|Failed HTTP/1 
transaction with key: 4)",
-            "Verify that ATS closed the fourth transaction.")
+            "Verify that ATS closed the forth transaction.")
+        tr.Processes.Default.Streams.stdout += Testers.ContainsExpression(
+            r"(Unexpected chunked content for key 5: too small|Failed HTTP/1 
transaction with key: 5)",
+            "Verify that ATS closed the fifth transaction.")
+        tr.Processes.Default.Streams.stdout += Testers.ContainsExpression(
+            r"(Unexpected chunked content for key 6: too small|Failed HTTP/1 
transaction with key: 6)",
+            "Verify that ATS closed the sixth transaction.")
 
         # ATS should close the connection before any body gets through. "def"
         # is the body sent by the server for each of these chunked cases.
diff --git 
a/tests/gold_tests/chunked_encoding/replays/malformed_chunked_header.replay.yaml
 
b/tests/gold_tests/chunked_encoding/replays/malformed_chunked_header.replay.yaml
index fd551941e6..ae135d77ab 100644
--- 
a/tests/gold_tests/chunked_encoding/replays/malformed_chunked_header.replay.yaml
+++ 
b/tests/gold_tests/chunked_encoding/replays/malformed_chunked_header.replay.yaml
@@ -42,12 +42,32 @@ sessions:
   - client-request:
       method: "POST"
       version: "1.1"
-      url: /large/chunk/size/header
+      url: /malformed/chunk/header2
       headers:
         fields:
         - [ Host, example.com ]
         - [ Transfer-Encoding, chunked ]
         - [ uuid, 2 ]
+      content:
+        transfer: plain
+        encoding: uri
+        # Chunk header sizes are in hex, so a size of `3z` is malformed.
+        data: 3z%0D%0Aabc%0D%0A0%0D%0A%0D%0A
+
+    # The connection will be dropped and this response will not go out.
+    server-response:
+      status: 200
+
+- transactions:
+  - client-request:
+      method: "POST"
+      version: "1.1"
+      url: /large/chunk/size/header
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ Transfer-Encoding, chunked ]
+        - [ uuid, 3 ]
       content:
         transfer: plain
         encoding: uri
@@ -70,7 +90,7 @@ sessions:
       headers:
         fields:
         - [ Host, example.com ]
-        - [ uuid, 3 ]
+        - [ uuid, 4 ]
 
     # The connection will be dropped and this response will not go out.
     server-response:
@@ -85,6 +105,29 @@ sessions:
         # Chunk header sizes are in hex, so a size of `z` is malformed.
         data: z%0D%0Adef%0D%0A0%0D%0A%0D%0A
 
+- transactions:
+  - client-request:
+      method: "GET"
+      version: "1.1"
+      url: /response/malformed/chunk/size2
+      headers:
+        fields:
+        - [ Host, example.com ]
+        - [ uuid, 5 ]
+
+    # The connection will be dropped and this response will not go out.
+    server-response:
+      status: 200
+      reason: OK
+      headers:
+        fields:
+        - [ Transfer-Encoding, chunked ]
+      content:
+        transfer: plain
+        encoding: uri
+        # Chunk header sizes are in hex, so a size of `1z` is malformed.
+        data: 1z%0D%0Adef%0D%0A0%0D%0A%0D%0A
+
 - transactions:
   - client-request:
       method: "GET"
@@ -93,7 +136,7 @@ sessions:
       headers:
         fields:
         - [ Host, example.com ]
-        - [ uuid, 4 ]
+        - [ uuid, 6 ]
 
     # The connection will be dropped and this response will not go out.
     server-response:

Reply via email to