JakeChampion / JakeChampion/trafficserver
[audit][hdrs] Comma-value mutators can leave list cells unprotected against heap coalescing (stale pointer / UAF)
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 0
- Forks
- 0
- Avg merge
- 8h 2m
- Merged PRs (30d)
- 21
Description
Severity: medium · Category: use-after-free
Location: src/proxy/hdrs/MIME.cc:1835
What's wrong
mime_field_value_str_from_strlist() protects the field's string heap from garbage-collection during its allocate_str() call with a single HeapGuard pinned to list->head->str, relying on the invariant stated in the comment at line 1834 that "all strings are from the same heap." Several callers violate that invariant by placing an off-heap pointer into the cell that becomes the list head, so the guard pins nothing while the remaining cells still point into the original (now unpinned) string heap. If allocate_str() at line 1851 triggers coalesce_str_heaps() (reachable when m_lost_string_space exceeds MAX_LOST_STR_SPACE, or when a demote fails because all ronly slots are occupied), the original rw/ronly heap can be evacuated and freed, leaving cells 1..n dangling; the memcpy(dest, cell->str, cell->len) at line 1861 then reads freed memory. Affected callers: mime_field_value_insert_comma_val() with idx==0 (list.prepend of a cell whose str is caller memory new_piece.data(), MIME.cc:1968-1979); mime_field_value_set_comma_val() when idx==0 sets head->str = new_piece.data() (MIME.cc:1889); mime_field_value_extend_comma_val() when idx==0 sets head->str = temp_ptr, a stack/ats_malloc buffer (MIME.cc:2045). These are reachable from plugins via the InkAPI TSMimeHdrFieldValue* entry points (e.g. InkAPI.cc:2403), with plugin-controlled idx>=0.
Evidence
HdrHeap::HeapGuard guard(heap, list->head->str); // MIME.cc:1835, comment: "all strings are from the same heap when it is split into the list."
// ... new_value = heap->allocate_str(new_value_len); (1851) ... memcpy(dest, cell->str, cell->len); (1861).
// Caller extend sets: cell->str = temp_ptr; (2045). Caller set does cell->str = new_piece.data(); (1889).
Suggested fix
Do not rely on head-cell-only pinning. Either build the reassembled value from a copy that is guaranteed heap-resident, guard every heap that any cell points into, or (simplest) construct the concatenated result in a temporary buffer first and only then call allocate_str + memcpy so no live cell pointer is read after a possible coalesce.
Filed from an automated multi-lens codebase audit. Full report: CODEBASE_AUDIT.md / audit-report.html on branch claude/codebase-audit-review-9nw7vz.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/proxy/hdrs/MIME.cc at lines 1835, 1851, and 1861, then trace the idx==0 callers at lines 1889, 1968-1979, and 2045. Check the InkAPI.cc entry point around line 2403 and determine how reassembly remains safe if allocate_str() coalesces heaps; done means no cell pointer can become dangling before it is copied.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100