Bug: Unbounded MCP resource pagination loops can cause OOM

Open 💬 0 comments Opened Jul 26, 2026 by afjal1

What version of Codex CLI is running?

Reproduced with codex-cli 0.145.0. The same implementation is still present in main at e4fb5311d7468839def62eabda4b268f4a54cf11.

What platform is your computer?

Darwin 26.5.2 arm64 arm (macOS).

What issue are you seeing?

MCP resource listing pagination loops have no maximum page count or total resource cap. A misbehaving or malicious MCP server could return a new (non-duplicate) cursor on every page, causing the client to loop indefinitely, accumulate unbounded memory, and potentially OOM.

Affected code locations:

  • codex-mcp/src/connection_manager/resources.rs:40-59
  • codex-mcp/src/connection_manager/resources.rs:97-122
  • codex-mcp/src/binding_clients.rs:92-115
  • codex-mcp/src/binding_clients.rs:133-158

Root cause: The pagination loops only check for duplicate cursors as a termination condition:

loop {
    let response = client.list_resources(params, timeout).await?;
    resources.extend(response.resources);
    match response.next_cursor {
        Some(next) if cursor.as_ref() == Some(&next) => {
            return Err(anyhow!("resources/list returned duplicate cursor"));
        }
        Some(next) => cursor = Some(next),
        None => return Ok(resources),
    }
}

A server that returns a new cursor on every page (e.g., cursor1, cursor2, cursor3, ...) would cause the client to loop indefinitely. Each page adds to the resources vector, so memory grows without bound.

Impact: Denial of service via OOM. A malicious or buggy MCP server could crash the Codex process by returning infinite pagination cursors.

What steps can reproduce the bug?

  1. Configure an MCP server that returns a new cursor on every resources/list call
  2. Start Codex CLI
  3. The client will loop indefinitely, consuming memory until OOM

What is the expected behavior?

The pagination loop should have a maximum page count (e.g., 100 pages) and break with a warning when exceeded.

Suggested fix

Add a MAX_RESOURCE_PAGES constant and break the loop when exceeded:

const MAX_RESOURCE_PAGES: usize = 100;

loop {
    let response = client.list_resources(params, timeout).await?;
    resources.extend(response.resources);
    if resources.len() > MAX_RESOURCE_PAGES * 1000 {
        warn!("resource listing exceeded maximum pages, stopping");
        break;
    }
    match response.next_cursor {
        Some(next) if cursor.as_ref() == Some(&next) => {
            return Err(anyhow!("resources/list returned duplicate cursor"));
        }
        Some(next) => cursor = Some(next),
        None => return Ok(resources),
    }
}

Related

  • #28858 — Codex does not follow MCP tools/list pagination via nextCursor (opposite problem)

Scope

Four call sites in two files. The fix is ~10 lines per call site. No behavioral change for正常 servers — only protection against misbehaving servers.

View original on GitHub ↗