Skip to main content
Vulnerability Database/CVE-2026-64847

CVE-2026-64847: AnyIO Process Pool DOS Vulnerability

CVE-2026-64847 is a denial of service flaw in AnyIO asynchronous framework that allows worker processes to block indefinitely through stderr pipe overflow. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-64847 Overview

CVE-2026-64847 is a resource exhaustion vulnerability in AnyIO, a high-level asynchronous concurrency and networking framework for Python that runs on top of Trio or asyncio. Versions prior to 4.14.2 start process-pool workers with standard error connected to a pipe that the parent process never drains. Worker code that writes enough attacker-influenced data to sys.stderr fills the pipe buffer and blocks before returning the standard-output protocol response. The awaiting to_process.run_sync() call then remains blocked indefinitely. The vulnerability is tracked as [CWE-770: Allocation of Resources Without Limits or Throttling].

Critical Impact

Applications running untrusted or faulty worker code that produces substantial stderr output can deadlock the AnyIO process pool, resulting in denial of service for asynchronous workloads.

Affected Products

  • AnyIO versions prior to 4.14.2
  • Python applications using anyio.to_process.run_sync() with untrusted worker code
  • Async frameworks and services that depend on AnyIO's process-pool workers

Discovery Timeline

  • 2026-09-18 - CVE-2026-64847 published to NVD
  • 2026-09-24 - Last updated in NVD database

Technical Details for CVE-2026-64847

Vulnerability Analysis

AnyIO's to_process module spawns worker subprocesses to execute synchronous functions off the main event loop. The documented behavior redirects all three standard streams (stdin, stdout, stderr) to os.devnull within the worker. The implementation prior to 4.14.2 only redirected stdin and stdout, leaving stderr connected to a pipe inherited from the parent process.

The parent process never reads from that stderr pipe. On POSIX systems, pipe buffers are bounded (typically 64 KB on Linux). When worker code writes more than the buffer capacity to sys.stderr, the write() call blocks waiting for the pipe to be drained. Because the worker blocks before it can send its response frame on stdout, the parent's await on the process-pool call never completes. The result is an indefinite deadlock consuming both worker and parent resources.

Root Cause

The root cause is an incomplete stream redirection in the worker bootstrap logic in src/anyio/to_process.py. Only sys.stdin and sys.stdout were reassigned to os.devnull; sys.stderr was left attached to the inherited pipe. The pipe was neither drained by a reader thread nor closed, allowing bounded-buffer backpressure to deadlock the worker.

Attack Vector

Exploitation requires local access with the ability to influence data written to sys.stderr inside a worker function. An attacker who controls worker input, or who can trigger error paths that emit large tracebacks or diagnostic output, can reliably fill the pipe buffer. This is most impactful in applications that execute untrusted user-supplied code inside process-pool workers, or in services where error output scales with attacker-controlled input.

python
     stdout = sys.stdout
     sys.stdin = open(os.devnull)
     sys.stdout = open(os.devnull, "w")
+    sys.stderr = open(os.devnull, "w")
 
     stdout.buffer.write(b"READY\n")
     while True:

Source: GitHub Commit f1b7301. The patch adds the missing sys.stderr redirection to os.devnull, matching the documented behavior and eliminating the pipe-buffer backpressure path.

Detection Methods for CVE-2026-64847

Indicators of Compromise

  • Python worker subprocesses spawned by AnyIO that remain in a blocked state on write() syscalls against a pipe file descriptor.
  • Async applications where to_process.run_sync() calls never complete and awaiting coroutines accumulate indefinitely.
  • Growing counts of orphaned or hung Python worker processes tied to a parent using AnyIO.

Detection Strategies

  • Inventory Python environments and identify installations of the anyio package at versions below 4.14.2 using pip list or software bill of materials (SBOM) tooling.
  • Instrument process-pool calls with timeouts and log any invocation that exceeds an expected upper bound, treating persistent hangs as candidate deadlocks.
  • Correlate worker process CPU/wall-time discrepancies where wall time grows but CPU usage remains near zero, indicative of blocked I/O.

Monitoring Recommendations

  • Monitor host telemetry for long-lived Python child processes whose parent is an AnyIO-based service, and alert when lifetimes exceed configured thresholds.
  • Track file-descriptor counts and pipe usage per process to detect accumulation of blocked worker pipes.
  • Log and review the size of stderr output produced by worker functions handling untrusted input, since anomalously large writes are the precursor to the deadlock.

How to Mitigate CVE-2026-64847

Immediate Actions Required

  • Upgrade AnyIO to version 4.14.2 or later in all Python environments that use anyio.to_process functionality.
  • Audit application code paths that pass untrusted input to worker functions executed via to_process.run_sync() and constrain the volume of diagnostic output.
  • Add wall-clock timeouts around process-pool invocations so a hung worker cannot indefinitely block callers.

Patch Information

The issue is fixed in AnyIO 4.14.2. The fix, tracked in Pull Request #1207 and shipped in the 4.14.2 release, redirects sys.stderr in the worker subprocess to os.devnull, matching the documented behavior. Details are also published in GitHub Security Advisory GHSA-5p39-cfhj-2xmp.

Workarounds

  • In worker functions, explicitly reassign sys.stderr to open(os.devnull, "w") at the top of the worker entry point until the upgrade is applied.
  • Suppress or cap stderr output produced by third-party libraries invoked inside workers by configuring logging handlers to write to files instead of the inherited stream.
  • Wrap to_process.run_sync() calls with anyio.fail_after() or equivalent timeout primitives to bound blocking time and recover from suspected deadlocks.
bash
# Upgrade AnyIO to the patched version
pip install --upgrade 'anyio>=4.14.2'

# Verify the installed version
python -c "import anyio; print(anyio.__version__)"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.