CVE-2022-2309 is a NULL pointer dereference vulnerability in the Python lxml library that lets an attacker crash an application by feeding malformed XML to the iterwalk function, causing a denial of service. The same code path backs the canonicalize function, so C14N serialization is affected too. The bug only triggers when lxml is paired with libxml2 versions 2.9.10 through 2.9.14; older libxml2 releases are not vulnerable.
It is a low-glamour bug with a high nuisance value. There is no code execution and no data exposure, but a single crafted document can take down a service that parses untrusted XML, which is enough to matter for anything internet-facing.
What actually goes wrong
lxml is a Python binding over the C library libxml2. The iterwalk helper walks an existing element tree and emits events as it visits nodes, similar in spirit to a pull parser but operating over an in-memory tree. When it encounters namespace data that is NULL or internally inconsistent, the helper dereferences a pointer that it assumed was valid. In C, dereferencing NULL means an immediate segmentation fault, and in a Python process that means the whole interpreter goes down, not just the request being handled.
Because the crash happens in native code, you cannot catch it with a Python try/except. The process is simply gone. In a synchronous single-worker deployment that is a full outage; in a multi-worker setup it burns a worker per malicious request, and an attacker can loop.
Which configurations are affected
This is the part teams get wrong, so it is worth being precise. Per the GitHub advisory, the vulnerability depends on the libxml2 version that lxml is linked against, not primarily on the lxml version:
Vulnerable when: libxml2 2.9.10 – 2.9.14
Not affected: libxml2 2.9.9 and earlier
Fixed by: lxml 4.9.1 (adds NULL/consistency checks
in the iterwalk/canonicalize path)
The fix ships in lxml 4.9.1. On many platforms lxml bundles its own libxml2, so upgrading lxml also pulls in a patched behavior. On Linux distributions that dynamically link the system libxml2, the picture is muddier: you may need both an lxml update and a libxml2 update from your distro.
To see what your environment is actually running:
import lxml.etree
print(lxml.etree.LXML_VERSION) # lxml itself
print(lxml.etree.LIBXML_VERSION) # linked libxml2
If LIBXML_VERSION falls in the 20910–20914 range and your lxml predates 4.9.1, you are exposed on any path that reaches iterwalk or canonicalize.
Do you even use the vulnerable path?
The advisory notes something practical: the vulnerable sequence (parse, then iterwalk) is not the idiomatic way to consume XML. For streaming, most code uses iterparse, which is more efficient and does not go through the affected helper. The legitimate reason to hit iterwalk is C14N canonicalization, for example XML converters that serialize to canonical form, or code that signs XML documents.
So there are two questions to answer:
- Is my linked libxml2 in the vulnerable range and my lxml below 4.9.1?
- Does my application call
iterwalkorcanonicalizeon data that could be attacker-controlled?
If the answer to the second is no, the practical risk is much lower even on a vulnerable build. A dependency scanner will answer the first question mechanically; the second is a grep and a code review. An SCA tool can flag the vulnerable lxml build across your services and its transitive users so you know which teams need to look at question two.
Remediation
Upgrade lxml to 4.9.1 or later. That is the single action that resolves it for the common case where lxml bundles libxml2:
pip install --upgrade "lxml>=4.9.1"
If your platform links the system libxml2 dynamically, also update libxml2 to 2.9.15 or later (or your distribution's patched 2.9.14 build). As a temporary workaround where an upgrade is blocked, the advisory's suggestion holds: replace iterwalk with iterparse where you can, since iterparse does not traverse the vulnerable code.
Rebuild and redeploy after upgrading. Because lxml is a compiled extension, a stale wheel cached in a base image can quietly keep the vulnerable version alive even after your requirements file says otherwise, so verify LIBXML_VERSION in the running container rather than trusting the lockfile.
FAQ
Can CVE-2022-2309 lead to remote code execution?
No. It is strictly a NULL pointer dereference that causes a crash, which is a denial of service. There is no memory corruption that leads to code execution in this CVE.
I upgraded lxml but my scanner still flags it. Why?
Two common causes: a cached compiled wheel in your build image kept the old version, or your platform dynamically links a system libxml2 that is still in the vulnerable 2.9.10 to 2.9.14 range. Check lxml.etree.LIBXML_VERSION in the actual runtime.
Which version of lxml fixes it?
lxml 4.9.1. It adds checks for NULL or inconsistent namespace attributes before they are accessed in the iterwalk and canonicalize paths.
Is my app at risk if I only use iterparse?
The specific crash lives in the iterwalk and canonicalize code path. If you never call those and your input never reaches C14N serialization, the exploitable surface is much smaller, though upgrading is still the right call.