GraphQL-core: Denial of service via memory leak in aborted validations
Impact
GraphQL-core versions 3.3.0a3 to 3.3.0 (excluding 3.3.1) have a denial of service vulnerability via memory leak in aborted validations, allowing any client to trigger memory growth with a small document containing more than 100 validation errors. No authentication is required. The impact is denial of service through memory exhaustion.
Summarized by @cf/meta/llama-3.3-70b-instruct-fp8-fast. Verify details against the original sources.Affected packages
Start with the recorded fix for your release branch. Check your installed version and test compatibility before upgrading. Fixing this advisory does not rule out other vulnerabilities in the release.
| Affected range | Recorded fix |
|---|---|
| From 3.3.0a3 before 3.3.1Ecosystem versions | 3.3.1 |
Version evidence: OSV · GHSA-mpqx-q82w-9x46
View original version evidence (18 explicit versions)
Affected products
Vendor version evidence. Product records are separate from automatic dependency matching.
graphql-python · GitHub_M
graphql-core
1 range
graphql-python · GitHub_M
graphql-core
Compare with this publisher’s ranges. This does not inspect your installation.
affected
>= 3.3.0a3, < 3.3.1
Other versions: unknown
Original source description
Summary
When a validation was aborted because the document contained more errors than allowed by max_errors (100 by default), validate() raised a single module-level ValidationAbortedError instance. Python appends the frames of every raise to the existing __traceback__ of an exception that is raised again, so every aborted validation extended the traceback of this shared error. That traceback kept the frames, and with them the document, the validation context and all errors of every aborted validation, alive for the lifetime of the process.
Details
Any client can trigger an aborted validation by sending a small document with more than 100 validation errors, e.g. 101 unknown fields. No authentication is required, and since validation runs before execution, depth limiters and complexity analyzers do not prevent it. Each such request permanently retained about 124 KiB on CPython 3.12, without an upper limit, so memory grew with every request until the process was restarted or killed.
GraphQL.js uses a shared error object at the same place, but JavaScript errors capture their stack trace only once when they are created, so it is not affected. GraphQL-core 3.2 creates a new error for each aborted validation and is not affected either.
Proof of concept
import gc
import tracemalloc
from graphql import build_schema, parse, validate
schema = build_schema("type Query { a: Int }")
document = parse("{ " + " ".join(f"f{i}" for i in range(101)) + " }")
tracemalloc.start()
validate(schema, document)
gc.collect()
base = tracemalloc.get_traced_memory()[0]
for _ in range(200):
validate(schema, document)
gc.collect()
print((tracemalloc.get_traced_memory()[0] - base) // 1024, "KiB")
With GraphQL-core 3.3.0 on CPython 3.12, the retained memory grows from 147 KiB after one call to about 25 MB after 200 calls. With the fix, it stays constant at about 180 KiB.
Impact
Denial of service through memory exhaustion. A few thousand small requests are enough to push a worker past a typical 256 MB or 512 MB container limit and trigger an OOM kill. No data is disclosed or modified.
Patches
Fixed in 3.3.1.
validate() now creates a new ValidationAbortedError for every aborted validation and removes its traceback and exception context before returning it, so that the returned errors do not keep the frames of the validation alive. The module-level validation_aborted_error instance, which was not exported, has been removed.
Workarounds
If you cannot upgrade, clear the traceback of the shared error after each validation:
from graphql.validation import validate as validate_document
from graphql.validation.validate import validation_aborted_error
def validate(*args, **kwargs):
try:
return validate_document(*args, **kwargs)
finally:
validation_aborted_error.__traceback__ = None
Pass this function instead of the original validate where your framework allows it.
Credits
Reported by @riodw. @Fire162 submitted a first fix in #288.
Inside the record
Evidence available
Describes the evidence in this view, not whether the product is safe.
Original records
CVE-2026-119286View the latest unmodified CVE JSON from the official source. It may be newer than this imported page.
cve.new JSON →Underlying weaknesses
The source’s classification of what caused the vulnerability.
Contributing organizations · 2
Assigning organization
Additional data publisher
What changed
Follow changes- CVE · CVE-2026-119286
Observed 2026-10-30T17:19:35.640Z · upstream updated 2026-10-30T15:58:44.342Z
Prioritization updated
before
[]
after
[ { "source": "CISA-ADP", "type": "SSVC", "role": "CISA Coordinator", "timestamp": "2026-10-30T15:47:35.331193Z", "decisions": [ { "name": "Exploitation", "value": "none" }, { "name": "Automatable", "value": "yes" }, { "name": "Technical Impact", "value": "partial" } ] } ]
Large field values are marked as truncated previews. Original source snapshots remain retained.
Showing up to 50 most recent observed changes.