JakeChampion / JakeChampion/trafficserver

[audit][hdrs] Comma-value mutators can leave list cells unprotected against heap coalescing (stale pointer / UAF)

Open
#29 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:hdrs audit severity:medium
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.