Skip to content

Artefact extraction: bound parser resource consumption (memory/decompression + worker), not just wall-clock #1379

Description

@Chris0Jeky

Context

Follow-up from #1369 / PR #1378, which added a per-extraction wall-clock budget (Artefacts:ExtractionTimeoutSeconds, default 30s). That bounds request latency: the request returns when the budget fires, and a runaway PdfPig PdfDocument.Open(...) is abandoned.

Gap

The wall-clock budget does not bound the parser's actual resource consumption:

  1. Abandoned-thread CPU. .NET cannot hard-kill a thread, so an abandoned parser-bomb parse keeps running to completion on a thread-pool thread (CPU + one pool thread held for the full parse). Concurrent bomb submissions could still pressure threads/CPU even though each request returns promptly.
  2. Unbounded abandoned-parse accumulation (adversarial-review confirmed residual, PR Bound artefact extraction with a wall-clock budget #1378 round 2). There is no cap on how many abandoned extractions can pile up: N concurrent parser-bomb submissions produce N spinning thread-pool threads that keep burning CPU long after each request recorded its extraction-timeout row and returned. A sustained submission rate above the parse-completion rate grows the abandoned set without bound — thread-pool starvation and CPU exhaustion — so the wall-clock timeout alone must NOT be treated as the DoS boundary. Also flagged independently by chatgpt-codex-connector on PR Bound artefact extraction with a wall-clock budget #1378.
  3. Memory / decompression ceiling. There is no bound on the expanded in-memory size (only the compressed 10 MiB input, page, and output-char caps). A FlateDecode decompression bomb that expands massively in memory could exhaust memory before the wall-clock budget fires. (Suggested in Artefact PDF extraction has no wall-clock/parser timeout — parser-bomb DoS (post-merge finding from #1346) #1369 as "add a decompression / allocation ceiling"; intentionally out of scope for the wall-clock slice.)

Why not now

ArtefactExtractionService has no caller yet (not wired to upload/HTTP/worker). The wall-clock budget is the necessary first guard. Real resource bounding needs a heavier design (bounded worker with an allocation/expanded-size ceiling, or process isolation for the parse) and should land before extraction is wired to any request path.

Acceptance (when picked up)

  • A configurable expanded/allocation ceiling for a single extraction (bound decompressed size, not just compressed input).
  • A hard concurrency cap on parser workers (bounded worker pool or bounded queue with rejection/backpressure) so the abandoned-parse set cannot grow without bound under sustained parser-bomb submissions — including a limit on concurrently abandoned (still-spinning) parses, not just concurrently active requests. This is a hard gate: no request-path or worker wiring of IArtefactExtractionService before this lands.
  • A strategy for the abandoned-parse CPU/thread cost (bounded worker pool for extraction, or out-of-process parse) so concurrent bombs cannot exhaust the shared thread pool.
  • A regression test using a decompression-bomb-shaped fixture asserting the ceiling aborts extraction and records the standard warning-bearing history row.
  • A regression/stress test asserting the concurrency cap holds when submissions exceed capacity (excess submissions are rejected or queued, never unbounded).

References

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions