JakeChampion / JakeChampion/trafficserver

[10.2][net] manage_active_queue/manage_keep_alive_queue cache a next pointer across _close_ne() callbacks that can free queue entries

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

Nobody has claimed this yet.

10.2 area:net audit severity:medium
Dominant language
C++
Stars
0
Forks
0
Avg merge
8h 2m
Merged PRs (30d)
21

Description

Branch: 10.2.x (10.2.1, commit 31f1f2f3b) · Severity: medium · category: use-after-free (latent)
Location: src/iocore/net/NetHandler.cc:428 (manage_active_queue) and :482 (manage_keep_alive_queue)

What's wrong

Both loops cache ne_next = ne->active_queue_link.next before calling _close_ne(ne, ...). _close_ne() invokes ne->callback(VC_EVENT_*TIMEOUT) → the state machine, which (while the NetHandler mutex is held) can synchronously do_io_close() a different same-thread queued VC; that close runs free_neteventremove_from_active_queue/remove_from_keep_alive_queue, freeing that VC. If the freed VC is the cached ne_next, the next iteration dereferences freed memory.

InactivityCop::check_inactivity deliberately avoids this with the cop_list.pop() pattern ("Use pop() to catch any closes caused by callbacks", UnixNet.cc:91) — the list stays authoritative because free_netevent removes from cop_list. These two queue walks were not converted.

Why this is a tracked issue, not a PR (yet)

A correct fix needs to mirror the pop-style traversal (re-derive from the authoritative queue rather than a cached raw pointer), which is a non-trivial rework of a hot path that runs under connection-pressure. It should be done with a targeted stress/ASAN test, which isn't feasible in the audit environment. Filing for a maintainer to take with proper testing. Reachable only when a timeout callback closes a different same-thread queued VC (e.g. HTTP/2 or cross-session teardown).

Suggested direction

Convert both loops to the cop_list-style pattern: pop/remove from the queue head and process, re-queuing entries that are kept, so a callback-driven free of any other entry can never leave a dangling iterator; or snapshot into a refcounted list before invoking callbacks.


From an automated multi-lens audit of the 10.2.x branch. Full report on branch claude/codebase-audit-review-9nw7vz (CODEBASE_AUDIT_10.2.md).

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/iocore/net/NetHandler.cc at manage_active_queue and manage_keep_alive_queue, then compare their traversal with the pop-style pattern in UnixNet.cc:91 and InactivityCop::check_inactivity. Rework the queue walk so callback-driven closes cannot leave a dangling next entry, and verify it with a targeted stress/ASAN test covering same-thread queued VC closures.

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
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.