Safeguard
Application Security•

CWE-1322 Blocking Code in a Single-Threaded, Non-Blocking Context

One blocking call in a Node.js or asyncio event loop can freeze every request. How attackers trigger it and how to keep the loop free.

Safeguard Security Team
3 min read

CWE-1322 describes a product built on a single-threaded, non-blocking model that still contains code which can block when called. If an attacker can reach that code, one request can stall everyone.

How it happens

Single-threaded event loops exist to avoid the resource cost of many threads. MITRE names Python asyncio, Vert.x and Node.js, plus custom event loops. In such a model all code should be non-blocking. If something blocks, the event loop effectively stops, and nothing else gets served.

Blocking code includes expensive computation and blocking library calls, such as exclusive file operations or calls that need a successful network operation. The risk is highest when an attacker can invoke that code directly, or when the blocking is affected by environmental conditions the attacker can influence.

A Python example. The vulnerable handler does a CPU heavy loop and a blocking network call inside an async function.

# Vulnerable: blocks the event loop for every client
import asyncio, requests

async def handler(request):
    data = requests.get(request.query["url"]).text   # blocking network call
    total = sum(i * i for i in range(10**8))         # long CPU loop
    return data, total

A safer shape moves both off the loop.

# Safer: use an async client and a worker for CPU work
import asyncio, aiohttp

def heavy():
    return sum(i * i for i in range(10**6))

async def handler(request):
    async with aiohttp.ClientSession() as s:
        async with s.get(request.query["url"], timeout=5) as r:
            data = await r.text()
    total = await asyncio.get_running_loop().run_in_executor(None, heavy)
    return data, total

(Fetching a user-supplied URL has its own risks. The point here is the loop.)

What an attacker gains

The consequence in the record is denial of service through CPU consumption. An unexpected call to blocking code can trigger an infinite loop, or a large loop that makes the software pause and wait indefinitely.

How to find it

The record has no detection methods. Practical checks: look for synchronous file, network and crypto calls inside async handlers, and measure event loop lag under load. Many runtimes have a debug mode that reports slow callbacks.

How to fix it

  1. Replace blocking calls with non-blocking alternatives used asynchronously.
  2. Pass expensive computations to worker threads. The right approach depends on the framework.
  3. Break expensive computations into several smaller ones. Check your framework documentation for guidance.

Quick check

Why does one blocking call affect all users? There is one thread running the event loop. While blocking code runs, the loop cannot process anything else.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.