Use of Blocking Code in Single-threaded, Non-blocking Context

Description

Use of Blocking Code in Single-threaded, Non-blocking Context occurs when the product uses a non-blocking model that relies on a single threaded process for features such as scalability, but it contains code that can block when it is invoked. Single-threaded non-blocking models (Python asyncio, Vert.x, Node.js) are designed to overcome resource constraints of multi-threaded approaches, but blocking code halts the event loop, compromising the model's intended benefits and potentially causing denial of service.

Risk

Blocking code in non-blocking contexts has severe implications. Event loop blocked. All concurrent operations stalled. Denial of service enabled. Application unresponsive. Scalability benefits lost. Resource starvation. Timeout cascades. User experience degraded. Database connections exhausted. API response times spike. High likelihood when mixing synchronous libraries with async frameworks.

Solution

Replace blocking calls with asynchronous non-blocking alternatives. Offload expensive computations to worker threads (framework-dependent). Decompose expensive computations into smaller, sequential operations that yield control back to the event loop. Audit all library calls for blocking behavior. Use async-compatible database drivers and HTTP clients.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

Blocking calls trigger infinite loops or extended iterations, causing indefinite pauses and denial of service through resource consumption.

Example Code

Vulnerable Code

// Vulnerable: Node.js with blocking operations

const express = require('express');
const fs = require('fs');
const crypto = require('crypto');
const app = express();

// VULNERABLE: Synchronous file read blocks the event loop
app.get('/file/:name', (req, res) => {
    try {
        // VULNERABLE: fs.readFileSync blocks until complete
        const content = fs.readFileSync(`/data/${req.params.name}`);
        res.send(content);
    } catch (err) {
        res.status(500).send('Error');
    }
    // While this reads a large file, ALL other requests wait
});

// VULNERABLE: CPU-intensive operation blocks event loop
app.get('/hash', (req, res) => {
    const data = req.query.data || '';

    // VULNERABLE: Expensive computation blocks
    // 100,000 iterations of SHA-256
    let hash = data;
    for (let i = 0; i < 100000; i++) {
        hash = crypto.createHash('sha256').update(hash).digest('hex');
    }

    res.send(hash);
    // All other requests blocked during computation
});

// VULNERABLE: Synchronous database-like operation
app.get('/search', (req, res) => {
    const results = [];
    const items = loadAllItems();  // Assume this returns millions of items

    // VULNERABLE: Long-running synchronous loop
    for (const item of items) {
        if (item.name.includes(req.query.q)) {
            results.push(item);
        }
    }

    res.json(results);
    // Event loop blocked for entire search
});

// VULNERABLE: Blocking network call
const request = require('sync-request');  // Synchronous HTTP!

app.get('/proxy', (req, res) => {
    // VULNERABLE: Synchronous HTTP request
    const response = request('GET', req.query.url);
    res.send(response.body);
    // Blocks until external request completes
});

app.listen(3000);

// Attack: Send requests to /hash endpoint
// Each request blocks all others for seconds
// Few concurrent attackers = complete DoS
# Vulnerable: Python asyncio with blocking code

import asyncio
import time
import hashlib
import requests  # Blocking HTTP library!

# VULNERABLE: Blocking function in async context
async def vulnerable_hash_data(data):
    # VULNERABLE: CPU-intensive blocking operation
    hash_result = data
    for i in range(100000):
        hash_result = hashlib.sha256(hash_result.encode()).hexdigest()
    return hash_result
    # This blocks the entire event loop!

# VULNERABLE: Using blocking HTTP library
async def vulnerable_fetch_url(url):
    # VULNERABLE: requests.get is blocking!
    response = requests.get(url)  # Blocks event loop
    return response.text

# VULNERABLE: Blocking file I/O
async def vulnerable_read_file(path):
    # VULNERABLE: Standard open() is blocking
    with open(path, 'r') as f:
        return f.read()  # Blocks on disk I/O

# VULNERABLE: time.sleep is blocking
async def vulnerable_delay(seconds):
    # VULNERABLE: time.sleep blocks the event loop
    time.sleep(seconds)  # Should use asyncio.sleep!

async def handle_request(data):
    # Even though this is async, it blocks
    result = await vulnerable_hash_data(data)
    return result

async def main():
    # These will NOT run concurrently!
    # Each blocking call stalls everything
    tasks = [
        handle_request("data1"),
        handle_request("data2"),
        handle_request("data3"),
    ]
    results = await asyncio.gather(*tasks)
    # Takes 3x as long as expected because each blocks
// Vulnerable: Vert.x with blocking code

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import java.io.*;
import java.net.*;

public class VulnerableVerticle extends AbstractVerticle {

    @Override
    public void start() {
        vertx.createHttpServer()
            .requestHandler(req -> {
                String path = req.path();

                if (path.equals("/file")) {
                    // VULNERABLE: Blocking file read
                    try {
                        // FileInputStream is blocking!
                        FileInputStream fis = new FileInputStream("/data/large.txt");
                        byte[] data = fis.readAllBytes();  // Blocks!
                        fis.close();
                        req.response().end(new String(data));
                    } catch (IOException e) {
                        req.response().setStatusCode(500).end();
                    }
                }

                if (path.equals("/external")) {
                    // VULNERABLE: Blocking HTTP request
                    try {
                        URL url = new URL(req.getParam("url"));
                        // URLConnection is blocking!
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        BufferedReader reader = new BufferedReader(
                            new InputStreamReader(conn.getInputStream())
                        );
                        StringBuilder response = new StringBuilder();
                        String line;
                        while ((line = reader.readLine()) != null) {
                            response.append(line);  // Blocks on each read!
                        }
                        req.response().end(response.toString());
                    } catch (Exception e) {
                        req.response().setStatusCode(500).end();
                    }
                }

                if (path.equals("/compute")) {
                    // VULNERABLE: CPU-intensive blocking computation
                    long result = 0;
                    for (long i = 0; i < 10000000000L; i++) {
                        result += Math.sqrt(i);  // Blocks event loop!
                    }
                    req.response().end(String.valueOf(result));
                }
            })
            .listen(8080);
    }
}

Fixed Code

// Fixed: Node.js with non-blocking operations

const express = require('express');
const fs = require('fs').promises;  // FIXED: Promise-based API
const crypto = require('crypto');
const { Worker } = require('worker_threads');
const app = express();

// FIXED: Asynchronous file read
app.get('/file/:name', async (req, res) => {
    try {
        // FIXED: fs.promises.readFile is non-blocking
        const content = await fs.readFile(`/data/${req.params.name}`);
        res.send(content);
    } catch (err) {
        res.status(500).send('Error');
    }
});

// FIXED: CPU-intensive work offloaded to worker thread
app.get('/hash', async (req, res) => {
    const data = req.query.data || '';

    try {
        // FIXED: Use worker thread for CPU-intensive work
        const hash = await runInWorker(data);
        res.send(hash);
    } catch (err) {
        res.status(500).send('Error');
    }
});

function runInWorker(data) {
    return new Promise((resolve, reject) => {
        const worker = new Worker(`
            const { parentPort, workerData } = require('worker_threads');
            const crypto = require('crypto');

            let hash = workerData;
            for (let i = 0; i < 100000; i++) {
                hash = crypto.createHash('sha256').update(hash).digest('hex');
            }
            parentPort.postMessage(hash);
        `, { eval: true, workerData: data });

        worker.on('message', resolve);
        worker.on('error', reject);
    });
}

// FIXED: Chunked processing for large datasets
app.get('/search', async (req, res) => {
    const items = await loadAllItemsAsync();
    const query = req.query.q;
    const results = [];

    // FIXED: Process in chunks, yielding to event loop
    const CHUNK_SIZE = 1000;
    for (let i = 0; i < items.length; i += CHUNK_SIZE) {
        const chunk = items.slice(i, i + CHUNK_SIZE);

        for (const item of chunk) {
            if (item.name.includes(query)) {
                results.push(item);
            }
        }

        // FIXED: Yield to event loop after each chunk
        await setImmediatePromise();
    }

    res.json(results);
});

function setImmediatePromise() {
    return new Promise(resolve => setImmediate(resolve));
}

// FIXED: Non-blocking HTTP client
const axios = require('axios');  // Async HTTP library

app.get('/proxy', async (req, res) => {
    try {
        // FIXED: Async HTTP request
        const response = await axios.get(req.query.url, {
            timeout: 5000  // Also add timeout!
        });
        res.send(response.data);
    } catch (err) {
        res.status(500).send('Error');
    }
});

app.listen(3000);
# Fixed: Python asyncio with non-blocking code

import asyncio
import hashlib
import aiohttp  # FIXED: Async HTTP library
import aiofiles  # FIXED: Async file library
from concurrent.futures import ProcessPoolExecutor

# FIXED: CPU-intensive work in process pool
executor = ProcessPoolExecutor(max_workers=4)

def cpu_intensive_hash(data):
    """Run in separate process to avoid blocking event loop."""
    hash_result = data
    for i in range(100000):
        hash_result = hashlib.sha256(hash_result.encode()).hexdigest()
    return hash_result

async def secure_hash_data(data):
    # FIXED: Run CPU-intensive work in process pool
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(executor, cpu_intensive_hash, data)
    return result

# FIXED: Using async HTTP library
async def secure_fetch_url(url):
    # FIXED: aiohttp is non-blocking
    async with aiohttp.ClientSession() as session:
        async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as response:
            return await response.text()

# FIXED: Using async file I/O
async def secure_read_file(path):
    # FIXED: aiofiles for non-blocking file I/O
    async with aiofiles.open(path, 'r') as f:
        return await f.read()

# FIXED: Use asyncio.sleep instead of time.sleep
async def secure_delay(seconds):
    # FIXED: asyncio.sleep yields to event loop
    await asyncio.sleep(seconds)

# FIXED: Chunked processing for large data
async def secure_process_large_list(items, query):
    results = []
    CHUNK_SIZE = 1000

    for i in range(0, len(items), CHUNK_SIZE):
        chunk = items[i:i + CHUNK_SIZE]

        for item in chunk:
            if query in item.get('name', ''):
                results.append(item)

        # FIXED: Yield to event loop
        await asyncio.sleep(0)

    return results

async def handle_request(data):
    # FIXED: Now truly concurrent
    result = await secure_hash_data(data)
    return result

async def main():
    # FIXED: These now run concurrently
    tasks = [
        handle_request("data1"),
        handle_request("data2"),
        handle_request("data3"),
    ]
    results = await asyncio.gather(*tasks)
    # Runs in parallel using process pool

asyncio.run(main())
// Fixed: Vert.x with non-blocking code

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.web.client.WebClient;

public class SecureVerticle extends AbstractVerticle {

    private WebClient webClient;

    @Override
    public void start() {
        webClient = WebClient.create(vertx);

        vertx.createHttpServer()
            .requestHandler(req -> {
                String path = req.path();

                if (path.equals("/file")) {
                    // FIXED: Non-blocking file read
                    vertx.fileSystem().readFile("/data/large.txt", result -> {
                        if (result.succeeded()) {
                            req.response().end(result.result());
                        } else {
                            req.response().setStatusCode(500).end();
                        }
                    });
                }

                if (path.equals("/external")) {
                    // FIXED: Non-blocking HTTP request
                    String url = req.getParam("url");
                    webClient.getAbs(url)
                        .timeout(5000)
                        .send(result -> {
                            if (result.succeeded()) {
                                req.response().end(result.result().bodyAsString());
                            } else {
                                req.response().setStatusCode(500).end();
                            }
                        });
                }

                if (path.equals("/compute")) {
                    // FIXED: CPU-intensive work on worker thread
                    vertx.executeBlocking(promise -> {
                        // This runs on worker pool, not event loop
                        long result = 0;
                        for (long i = 0; i < 10000000000L; i++) {
                            result += Math.sqrt(i);
                        }
                        promise.complete(result);
                    }, result -> {
                        if (result.succeeded()) {
                            req.response().end(String.valueOf(result.result()));
                        } else {
                            req.response().setStatusCode(500).end();
                        }
                    });
                }
            })
            .listen(8080);
    }
}

CVE Examples

  • CVE-2020-8203: Node.js application DoS through blocking operations in event loop.
  • CVE-2019-10790: Event loop blocking in Express.js middleware caused denial of service.

  • CWE-834: Excessive Iteration (parent)
  • CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop') (can follow)
  • CWE-557: Concurrency Issues (category)
  • CAPEC-25: Forced Deadlock

References

  1. MITRE Corporation. "CWE-1322: Use of Blocking Code in Single-threaded, Non-blocking Context." https://cwe.mitre.org/data/definitions/1322.html
  2. Node.js. "Don't Block the Event Loop"
  3. Python. "asyncio - Asynchronous I/O"
  4. Vert.x. "Golden Rule - Don't Block the Event Loop"