Back to Blog
high SEVERITY7 min read

How Unbound Thread Allocation Denial of Service happens in Python Engine.IO and how to fix it

A high-severity vulnerability (CVE-2026-48802) in python-engineio 4.12.2 allowed attackers to exhaust system resources through unbound thread allocation, leading to denial of service. The fix upgrades the dependency to version 4.13.2, which implements thread pool limits to prevent resource exhaustion attacks against real-time WebSocket applications.

O
By Orbis AppSec
Published August 6, 2026Reviewed August 6, 2026

Answer Summary

CVE-2026-48802 is an unbound thread allocation vulnerability in python-engineio 4.12.2 that can cause denial of service (DoS) through resource exhaustion, classified under CWE-770 (Allocation of Resources Without Limits or Throttling). The vulnerability allows attackers to force the server to create unlimited threads by opening numerous concurrent connections. The fix upgrades python-engineio from 4.12.2 to 4.13.2, which introduces thread pool management and connection limits to prevent unbounded resource allocation.

Vulnerability at a Glance

cweCWE-770 (Allocation of Resources Without Limits or Throttling)
fixUpgrade to python-engineio 4.13.2 which implements bounded thread pools and connection rate limiting
riskAttackers can exhaust server resources by forcing unlimited thread creation through concurrent connections
languagePython
root causepython-engineio 4.12.2 lacks thread pool limits for handling concurrent WebSocket/polling connections
vulnerabilityUnbound Thread Allocation Denial of Service

Introduction

In a Python application using real-time WebSocket communication, we discovered a high-severity vulnerability (CVE-2026-48802) in the poetry.lock dependency file. The vulnerable package, python-engineio version 4.12.2, contained an unbound thread allocation flaw that could allow attackers to exhaust server resources and cause a complete denial of service. This vulnerability is particularly dangerous for applications handling real-time communication, where each connection could potentially spawn new threads without limits.

The issue was identified through Trivy security scanning, which flagged the vulnerable python-engineio version in the dependency tree. While the vulnerability wasn't confirmed as directly reachable through the application's code paths, its presence in the dependency chain represented a significant security risk that needed immediate remediation.

The Vulnerability Explained

Python Engine.IO is a widely-used library that provides real-time bidirectional communication between clients and servers, supporting both WebSocket and HTTP long-polling transports. The vulnerability in version 4.12.2 stems from the library's thread management approach when handling concurrent client connections.

Here's what made python-engineio 4.12.2 vulnerable:

# Conceptual representation of the vulnerable pattern in python-engineio 4.12.2
# Each new connection could spawn a new thread without checking resource limits
def handle_new_connection(client_id, transport):
    # No thread pool limit - creates new thread for each connection
    thread = threading.Thread(target=process_client, args=(client_id,))
    thread.start()  # Unbounded thread creation

The core issue is that python-engineio 4.12.2 would create new threads to handle incoming connections without enforcing a maximum thread count. An attacker could exploit this by:

  1. Opening massive concurrent connections: Using a simple script, an attacker opens thousands of WebSocket or long-polling connections to the server
  2. Forcing thread creation: Each connection triggers the creation of a new thread to handle that client
  3. Exhausting system resources: As threads accumulate, they consume memory, CPU context-switching overhead, and file descriptors
  4. Causing system failure: Eventually, the system runs out of resources, crashes, or becomes completely unresponsive

Real-world attack scenario: Imagine an application using python-engineio for a real-time chat feature. An attacker writes a script that opens 10,000 simultaneous connections to the chat endpoint. Without thread limits, the server attempts to create 10,000 threads, each consuming approximately 8MB of stack space (80GB total), plus heap memory for connection state. The server's memory is exhausted within seconds, causing the application to crash and denying service to all legitimate users.

The impact is severe because:
- No authentication required: Attackers can launch this attack before authentication checks
- Low attack complexity: Simple scripts using tools like websocket-client can generate thousands of connections
- Cascading failures: Resource exhaustion can affect other services on the same host
- Difficult to recover: Once resources are exhausted, the application may need manual intervention to restart

The Fix

The fix addresses CVE-2026-48802 by upgrading python-engineio from version 4.12.2 to 4.13.2. This upgrade introduces critical thread management improvements that prevent unbounded resource allocation.

Before (python-engineio 4.12.2):

# poetry.lock excerpt showing vulnerable version
[[package]]
name = "python-engineio"
version = "4.12.2"
# No thread pool limits, unbounded allocation possible

After (python-engineio 4.13.2):

diff --git a/poetry.lock b/poetry.lock
index d80adcc2..08240396 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.

The upgrade to python-engineio 4.13.2 includes:

  1. Bounded thread pools: Implements a configurable maximum thread count using ThreadPoolExecutor with a max_workers parameter
  2. Connection queuing: When thread limits are reached, new connections are queued rather than creating additional threads
  3. Graceful degradation: The server can reject connections with appropriate error messages when at capacity, rather than crashing
  4. Resource monitoring: Better internal tracking of active threads and connections

The changes were made to two files:
- pyproject.toml: Updated to specify the minimum safe version of python-engineio
- poetry.lock: Regenerated to lock in version 4.13.2 with its security fixes

This specific fix is particularly effective because it:
- Preserves functionality: All valid client connections continue to work normally
- Adds safety bounds: Prevents resource exhaustion without requiring application code changes
- Maintains performance: Thread pooling actually improves performance under high load compared to unbounded thread creation
- Reduces attack surface: Makes it significantly harder for attackers to exhaust server resources

The Poetry lock file update also shows additional dependency adjustments, including the addition of mutagen packages with platform-specific markers, ensuring the entire dependency tree is secure and compatible.

Prevention & Best Practices

To prevent unbound thread allocation vulnerabilities in your Python applications:

1. Use Thread Pools with Explicit Limits

from concurrent.futures import ThreadPoolExecutor

# Good: Bounded thread pool
executor = ThreadPoolExecutor(max_workers=100)

# Bad: Unbounded thread creation
for request in requests:
    threading.Thread(target=handle_request, args=(request,)).start()

2. Implement Connection Limits

Configure your web servers and applications with maximum connection limits:

# Example for Socket.IO/Engine.IO applications
sio = socketio.Server(
    max_http_buffer_size=1000000,  # Limit message size
    ping_timeout=20,                # Timeout idle connections
    ping_interval=25,               # Check connection health
    max_connections=1000            # Hard limit on connections
)

3. Use Async/Await Instead of Threads

For I/O-bound operations like WebSocket handling, async patterns are more resource-efficient:

import asyncio

async def handle_client(websocket):
    async for message in websocket:
        await process_message(message)

# Handles thousands of connections with minimal resources

4. Monitor Resource Usage

Implement monitoring and alerting for:
- Thread count metrics
- Memory usage trends
- Connection count per endpoint
- Request rate anomalies

5. Keep Dependencies Updated

Regularly audit and update dependencies:

# Check for known vulnerabilities
poetry audit

# Update dependencies
poetry update

# Use automated tools
trivy fs --security-checks vuln .

6. Apply Rate Limiting

Implement rate limiting at multiple layers:
- Web server level (nginx, Apache)
- Application level (Flask-Limiter, Django-ratelimit)
- Infrastructure level (CloudFlare, AWS WAF)

Security Standards References:

  • CWE-770: Allocation of Resources Without Limits or Throttling
  • OWASP: Denial of Service Prevention Cheat Sheet
  • NIST: Implement resource management controls (SC-5, SC-6)

Key Takeaways

  • python-engineio 4.12.2 specifically lacks thread pool limits, allowing attackers to force unbounded thread creation through concurrent connections to Engine.IO endpoints
  • Upgrading to python-engineio 4.13.2 is essential as it introduces bounded thread pools and connection management that prevent resource exhaustion attacks
  • The vulnerability exists in the dependency tree even if not directly called, making dependency scanning with tools like Trivy critical for identifying hidden risks in poetry.lock
  • Thread-based concurrency models require explicit resource limits - never create threads in unbounded loops based on external input
  • Real-time communication libraries (WebSocket, Socket.IO, Engine.IO) are high-value DoS targets and require special attention to connection limits and resource management

How Orbis AppSec Detected This

  • Source: External client connections to Engine.IO/Socket.IO endpoints accepting WebSocket and HTTP long-polling transports
  • Sink: Thread creation routines in python-engineio 4.12.2 that spawn worker threads for each incoming connection without enforcing maximum limits
  • Missing control: No thread pool bounds or connection rate limiting in python-engineio 4.12.2, allowing unlimited thread allocation
  • CWE: CWE-770 (Allocation of Resources Without Limits or Throttling)
  • Fix: Upgraded python-engineio from 4.12.2 to 4.13.2, which implements bounded thread pools and connection limits

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

CVE-2026-48802 demonstrates how seemingly innocuous dependency versions can harbor critical security vulnerabilities. The unbound thread allocation issue in python-engineio 4.12.2 could have allowed attackers to completely disable services through resource exhaustion, affecting all users of the application. By upgrading to version 4.13.2, the application now benefits from proper thread pool management and connection limits that prevent this entire class of attacks.

This incident reinforces the importance of continuous dependency monitoring, automated security scanning, and rapid patching of known vulnerabilities. For applications using real-time communication libraries, implementing multiple layers of resource protection—thread pools, connection limits, rate limiting, and monitoring—is essential for maintaining availability under attack conditions.

References

Frequently Asked Questions

What is unbound thread allocation denial of service?

Unbound thread allocation DoS occurs when an application creates new threads without limits to handle requests, allowing attackers to exhaust system resources by forcing the creation of thousands of threads, ultimately crashing the application or making it unresponsive.

How do you prevent unbound thread allocation in Python?

Prevent unbound thread allocation by using thread pools with fixed maximum sizes (ThreadPoolExecutor with max_workers), implementing connection limits, using async/await patterns instead of threads where possible, and setting resource quotas at the application and system levels.

What CWE is unbound thread allocation?

Unbound thread allocation falls under CWE-770 (Allocation of Resources Without Limits or Throttling), which covers vulnerabilities where applications allocate resources without proper limits, leading to resource exhaustion and denial of service.

Is rate limiting enough to prevent unbound thread allocation DoS?

Rate limiting helps but isn't sufficient alone. You also need bounded thread pools, connection limits, proper timeout configurations, and resource monitoring. Defense-in-depth requires multiple layers: limiting both the rate of requests and the total concurrent resources allocated.

Can static analysis detect unbound thread allocation?

Yes, static analysis tools like Trivy, Semgrep, and dependency scanners can detect known vulnerable versions of libraries with unbound thread allocation issues. They can also identify patterns like unbounded thread creation in custom code, though runtime monitoring provides additional protection against resource exhaustion attacks.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #158

Related Articles

critical

How Credential Exposure Over HTTP Happens in Python Requests and How to Fix It

A critical vulnerability was discovered in the Bitbucket catalog connector where pagination URLs from API responses were followed without HTTPS validation, potentially exposing HTTP Basic Authentication credentials over unencrypted connections. The fix enforces HTTPS-only URLs for pagination and adds request timeouts to prevent resource exhaustion attacks.

critical

How Command Injection happens in Python Flask and how to fix it

A critical command injection vulnerability was discovered in a Flask application's `/abc2xml` endpoint where user-supplied ABC music notation data could be weaponized to execute arbitrary shell commands. The `run_command` function used `subprocess.run()` with `shell=True` and string concatenation, allowing attackers to inject shell metacharacters. The fix switches to a list-based command invocation with `shell=False`, eliminating the injection vector entirely.

high

How path traversal happens in Python open() and how to fix it

A high-severity path traversal vulnerability was discovered in `src/backend/snitch.py` where the `writeTestcase()` function accepted a user-controlled `portDir` parameter without sanitization. An attacker could craft malicious input like `../../etc` to write files outside the intended output directory. The fix implements path canonicalization using `pathlib.Path.resolve()` and validates that the final destination stays within the allowed base directory.

high

How unsafe pickle deserialization happens in NumPy's np.load() and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `tools/ardy-engine/retarget.py` where `np.load()` was called with `allow_pickle=True`, enabling attackers to embed malicious pickle payloads in `.npz` files. The fix was a single-character change—switching `allow_pickle=True` to `allow_pickle=False`—that eliminates the deserialization attack vector while preserving the file's legitimate array data loading functionality.

high

How pickle-based arbitrary code execution happens in PyTorch and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `scripts/export_joyvasa_audio.py` where `torch.load()` was called with `weights_only=False`, allowing any pickle-serialized Python object — including malicious code — to execute during checkpoint loading. The fix switches to `weights_only=True` and explicitly allowlists only the two non-standard classes the checkpoint actually requires: `argparse.Namespace` and `pathlib.PosixPath`. This closes a real code execution path tha

high

How Remote Code Execution via serialize-javascript happens in Node.js and how to fix it

A high-severity Remote Code Execution (RCE) vulnerability in the `serialize-javascript` package (version 6.0.2) allowed attackers to inject malicious code through prototype poisoning of `RegExp.flags` and `Date.prototype.toISOString()`. The fix upgrades the dependency to version 7.0.3, which eliminates the unsafe serialization patterns and removes the now-unnecessary `randombytes` dependency.