Copilot commented on code in PR #13636:
URL: https://github.com/apache/trafficserver/pull/13636#discussion_r3967814266


##########
src/proxy/hdrs/XPACK.cc:
##########
@@ -135,6 +135,8 @@ xpack_decode_string(Arena &arena, char **str, uint64_t 
&str_length, const uint8_
 
     len = huffman_decode(*str, str_len, p, encoded_string_len);
     if (len < 0) {
+      // Release the temporary area, otherwise it is left outstanding until 
the arena dies.
+      arena.str_free(*str);
       return XPACK_ERROR_COMPRESSION_ERROR;
     }

Review Comment:
   On the Huffman failure path, `*str` is freed but left pointing to freed 
arena memory (dangling). Given the note in the PR description that some call 
paths may incorrectly dereference this pointer after an error, this change can 
turn a leak into an input-triggerable use-after-free. Prefer decoding into a 
local temporary pointer and only assigning `*str` on success, or at minimum set 
`*str = nullptr` (and consider `str_length = 0`) after `arena.str_free(*str)` 
before returning.



##########
src/proxy/hdrs/unit_tests/test_XPACK.cc:
##########
@@ -155,6 +155,42 @@ TEST_CASE("XPACK_String", "[xpack]")
     }
   }
 
+  SECTION("failed huffman decoding releases the temporary area")
+  {
+    // 0x88 is the huffman flag plus a length of 8. An all-ones payload is not 
a
+    // decodable huffman sequence, so huffman_decode() fails after the 
temporary
+    // area has already been allocated out of the arena.
+    uint8_t bad_huffman[]   = "\x88\xff\xff\xff\xff\xff\xff\xff\xff";
+    int     bad_huffman_len = 9;

Review Comment:
   Initializing a `uint8_t[]` from a string literal implicitly appends a NUL 
terminator, while `bad_huffman_len` is hard-coded to 9. This is correct today 
but brittle (easy to desync if the literal changes). Consider switching to an 
explicit byte initializer list and deriving the length from 
`sizeof(bad_huffman)` to keep the buffer and length consistent.



##########
src/proxy/http3/QPACK.cc:
##########
@@ -1170,6 +1172,8 @@ QPACK::_on_encoder_stream_read_ready(IOBufferReader 
&reader)
       QPACKDebug("Received Insert Without Name Ref: name=%.*s, value=%.*s", 
static_cast<int>(name_len), name,
                  static_cast<int>(value_len), value);
       this->_dynamic_table.insert_entry(name, name_len, value, value_len);
+      // Free in reverse allocation order so Arena rewinds both entries.
+      this->_arena.str_free(value);
       this->_arena.str_free(name);

Review Comment:
   The QPACK free-order fix is not covered by an automated test (the new 
coverage focuses on XPACK’s failure path). If feasible, add a focused 
regression test that exercises these QPACK paths repeatedly and asserts the 
arena does not grow (similar to the new XPACK test’s 'baseline pointer' 
approach), to prevent future reordering regressions.



##########
src/proxy/http3/QPACK.cc:
##########
@@ -818,8 +819,9 @@ QPACK::_decode_literal_header_field_without_name_ref(const 
uint8_t *buf, size_t
   QPACKDebug("Decoded Literal Header Field Without Name Ref: name=%.*s, 
value=%.*s", static_cast<uint16_t>(name_len), name,
              static_cast<uint16_t>(value_len), value);
 
-  this->_arena.str_free(name);
+  // Free in reverse allocation order so Arena rewinds both entries.

Review Comment:
   The comment is phrased as a guaranteed outcome, but per the PR description 
`Arena::free()` may not rewind in some cases (e.g., when only a single block 
exists / last block not inspected). Consider softening the wording to reflect 
intent (e.g., '...so Arena can rewind...') to avoid overpromising behavior.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to