Where
-Infinity
0
Severity
7
Use After Free, Double Free

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!

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203