Skip to content

file_server: use-after-free in FileStreamer when async-file callback fires after request cancel #45152

@omkhar

Description

@omkhar

Description

FileStreamer::start() in source/extensions/filters/http/file_server/file_streamer.cc passes [this] captures to AsyncFileManager::stat() and AsyncFileManager::read():

cancel_callback_ = file_server_config_->asyncFileManager()->stat(
    dispatcher_, file_path_.string(), [this](absl::StatusOr<struct stat> result) {
      // 'this' freed if FileStreamer was destroyed before stat completed
      ...
    });

The async-file manager guarantees the callback eventually runs (cancellation does not prevent the callback from firing). If the request is cancelled (downstream RST, stream timeout, filter chain destruction) before the async-file operation completes, the callback fires against a destroyed FileStreamer and dereferences freed memory.

Reproduction

  1. Configure the file_server filter to serve a path backed by a slow AsyncFileManager (large file, or a backend that is intentionally throttled).
  2. Send a GET and quickly cancel / reset the request before file_server sees the stat / read callback.
  3. Repeat under load — ASAN surfaces the UAF in FileStreamer::start()'s stat callback or FileStreamer::read()'s read callback.

Severity / scope

CWE-416 use-after-free; reachable from any deployment that uses the file_server HTTP filter when downstream clients can cancel or reset requests while an async-file operation is in flight. Heap-grooming under the right object lifetimes can produce remote code execution; otherwise the worker process crashes.

Proposed fix

Hold a std::shared_ptr<bool> sentinel in FileStreamer and capture [this, alive = std::weak_ptr<bool>(alive_)] in the async-file callbacks. When the sentinel cannot be locked the callback short-circuits to a no-op. Pull request follows.

Background

This was first reported privately as GHSA-c4w8-wqj5-4xph. The envoy security team (@wbpcode) asked for this to be fixed in the open given the file_server filter's WIP status, so I'm filing here per their redirect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions