brbzull0 opened a new pull request, #13636:
URL: https://github.com/apache/trafficserver/pull/13636

   `Arena::free()` (`src/tscore/Arena.cc:129`) only rewinds when the freed range
   ends exactly at the block's water level:
   
   ```c
   if (b->m_water_level == (static_cast<char *>(mem) + size)) {
     b->m_water_level = static_cast<char *>(mem);
     return;
   }
   ```
   
   So releasing an earlier allocation while a later one is still outstanding is 
a
   silent no-op, and that space is not reclaimed for the lifetime of the arena.
   Two QPACK sites got the order wrong:
   
   - `_decode_literal_header_field_without_name_ref()` (`QPACK.cc:827`) 
allocated
     `name`, then `value`, then freed `name` first. The `name` free did nothing 
and
     only `value` was reclaimed.
   - The Insert Without Name Ref branch of `_on_encoder_stream_read_ready()`
     (`QPACK.cc:1180`) freed `name` and never freed `value` at all.
   
   Because the QPACK arena lives as long as the connection, both cases 
accumulate
   per decoded field rather than per request.
   
   ### Change
   
   Free `value` before `name` at both sites, so each free lands at the water 
level
   and both entries rewind. Also release whatever `xpack_decode_string()` 
allocated
   before returning failure -- it can allocate on the Huffman path and then 
fail,
   so the error return needs the same treatment (`value` is initialised to
   `nullptr` so the guard is meaningful).
   
   ### Test
   
   No test. This is worth being explicit about rather than glossing:
   
   `Arena` exposes no water-level or used-bytes accessor, so there is nothing to
   assert against from a unit test. And the leak is invisible to ASan and LSan
   because `Arena::~Arena()` calls `reset()`, which frees every block at 
connection
   close -- so nothing is leaking at process exit, the arena just grows while 
the
   connection is alive.
   
   Making this testable would mean adding an accessor to `Arena` for the sake of
   the test, which seemed worse than submitting it with the reasoning above and 
the
   `Arena::free()` behaviour cited directly.
   
   Run as a no-regression check, 4/4 pass: `h3_proxy_verifier`, 
`h3_python_client`,
   `h3_stream_lifetime`, `h3_flow_control` -- these drive real QPACK header
   exchange through both the decode path and the encoder stream.
   
   ### Note on scope
   
   The original change also touched `_read_insert_without_name_ref()`. That 
hunk is
   omitted here: its frees sit inside an
   `if (xpack_decode_string(...) < 0 && tmp > 0xFFFF)` body, and with that `&&` 
the
   body is unreachable, so the code would be dead on arrival. It belongs with a
   change that corrects those guards, not with this one.
   


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