CVE-2026-74860: Libxml2: double-free/uaf in libxml2 python bindings
A flaw was found in libxml2 with Python bindings enabled. A remote attacker could exploit this vulnerability by providing a specially crafted XML document containing a Document Type Definition (DTD) with enumerated attribute values. This triggers a double-free error in the SAX attributeDecl callback handler, where a string is freed twice. This flaw can lead to a denial of service (DoS) due to a reproducible crash in Python applications using the libxml2 SAX bindings.
Other sources
SUMMARY A double-free / use-after-free exists in the SAX attributeDecl callback handler (pythonAttributeDecl in python/libxml.c). When parsing XML containing a DTD <!ATTLIST> declaration with enumerated attribute values, each value string is freed twice: PyListSetItem() steals the reference, and an explicit PyDECREF() then drops the refcount to zero and frees the object, leaving a dangling pointer in the list.
AFFECTED COMPONENT
libxml2 with Python bindings enabled (python3-libxml2 / libxml2-python)
The defect is long-standing; the affected code in pythonAttributeDecl has been unchanged for many years.
Reproduced on upstream commit 4b35628e97472eaf23d8a841d2f711f7c2f96255 (2026-02-24).
IMPACT
Denial of service: 100% reproducible crash (SIGSEGV) in any Python application that uses the libxml2 SAX bindings (libxml2.createPushParser), registers an attributeDecl handler, and parses untrusted XML with a DTD <!ATTLIST> containing enumerated values. Verified 10/10 in isolated processes.
The use-after-free is also potentially exploitable for code execution: I was able to demonstrate hijacking the freed object's tpdealloc function pointer in-process. Full remote code execution would require additional heap grooming and is not demonstrated. I'd defer to your team on final severity scoring; I'd characterize the reliably demonstrated impact as DoS, with code execution as a credible but conditional escalation.
ROOT CAUSE (python/libxml.c, pythonAttributeDecl)
for (node = tree; node != NULL; node = node->next) { newName = PYIMPORTSTRING((char ) node->name); PyListSetItem(nameList, count, newName); / steals reference / PyDECREF(newName); / double-free / count++; }
PyListSetItem() does not increment the refcount, so the subsequent PyDECREF() over-decrements. Because of CPython pymalloc free-list reuse, list entries can end up pointing at the same freed address; cleanup after the SAX callback then decrefs dangling pointers and corrupts allocator state.
PROOF OF CONCEPT (minimal DoS)
import libxml2 class Handler: def attributeDecl(self, args): pass def startElement(self, a): pass def endElement(self, a): pass def characters(self, a): pass xml = b'''<?xml version="1.0"?> <!DOCTYPE r [ <!ELEMENT r EMPTY> <!ATTLIST r a (xx|yy|zz|ww|qq) "xx"> ]> <r a="xx"/>''' h = Handler() c = libxml2.createPushParser(h, "", 0, "t") c.parseChunk(xml, len(xml), 1) # SIGSEGV
UPSTREAM FIX (already merged and closed)
Fix: remove the erroneous PyDECREF(newName) since PyListSetItem already takes ownership.
Merge request: https://gitlab.gnome.org/GNOME/libxml2/-/mergerequests/397 ("python: Do not decref string after adding to the list")
Fix commit: 046931e6
Issue report: https://gitlab.gnome.org/GNOME/libxml2/-/workitems/1076
Upstream maintainer Nick Wellnhofer confirmed it appears to be a security issue and asked that a CVE be requested; the maintainer who merged the fix indicated CVE assignment is not handled by the project itself.
SUGGESTED CLASSIFICATION
CWE-415 (Double Free), leading to CWE-416 (Use After Free)
ENVIRONMENT
OS: Ubuntu 22.04 x8664; Python 3.10.12; GCC 11.4.0; libxml2 built from source with -fsanitize=address.
I'm happy to provide the full ASan trace or the code-execution PoC on request. Please let me know if you need anything else to proceed.
Thank you, Adnan Jakati!https://mailtrack.io/trace/mail/773e02eeb348f92b41f2a5d93b4c46af0007ab1f.png?u=12519192!
— Red Hat
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
debian/libxml2to a version that resolves this vulnerability.Fixed in 2.15.4+dfsg-1 - Upgrade
Upgrade
libxml2 with Python bindings (python3-libxml2 / libxml2-python)to a version that resolves this vulnerability.Patch 046931e6
Event History
Frequently Asked Questions
Which applications are exposed to this issue?
Python applications that use the libxml2 SAX bindings are exposed when they parse attacker-controlled XML. The vulnerable condition requires libxml2 to be built or used with Python bindings enabled.
What must an attacker provide to trigger the flaw?
An attacker needs to supply a specially crafted XML document containing a DTD with enumerated attribute values. Exploitation is remote, but the attacker must be able to cause the application to parse that XML.
What is the confirmed impact?
The described, reproducible impact is a crash and resulting denial of service in affected Python applications. The underlying error is a double-free in the SAX attributeDecl callback handler.