Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32k
gh-133009: fix UAF inxml.etree.ElementTree.Element.__deepcopy__
#133010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This is not enough. "tag", "text" and "tail" can be set to different value during deepcopying. If it was the last reference, thedeepcopy()
argument can be destroyed, and the reference can became handling.
The safe way is to increase the reference count of thedeepcopy()
argument before calling any code that can release the GIL.
I actually tried to do something with tag etc, but I wasn't able to crash the interpreter. |
String literals are interned and saved in the constants list. You need to use something having a single reference. Try |
picnixz commentedApr 26, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I've actually removed this code because I thought it wasn't needed, but I'll check with an evil non-interned string tomorrow. |
I'll try to add more tests later as well. |
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Modules/_elementtree.c Outdated
@@ -899,6 +905,8 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo) | |||
if (Py_REFCNT(object) == 1) { | |||
if (PyDict_CheckExact(object)) { | |||
// Exact dictionaries do not execute arbitrary code as it's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@serhiy-storchaka is this assumption correct? namely, here I don't need to increfobject
temporarily right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
PyDict_Next()
does not use__iter__
, so this comment is redundant.PyDict_Next()
does not call any user code.
Modules/_elementtree.c Outdated
@@ -899,6 +905,8 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo) | |||
if (Py_REFCNT(object) == 1) { | |||
if (PyDict_CheckExact(object)) { | |||
// Exact dictionaries do not execute arbitrary code as it's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
PyDict_Next()
does not use__iter__
, so this comment is redundant.PyDict_Next()
does not call any user code.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Actually, it's much more tricky than what I thought. Even with the INCREF/DECREF and the additional checks, the following still crashes, but not during classEvil(ET.Element):def__deepcopy__(self,memo):root.append(ET.Element('y'))root.append(ET.Element('z'))returnselfY=Evil('y')root=ET.Element('a')root.extend([Evil('x'),ET.Element('t'),Y])c=deepcopy(root)print(list(c))print("ok")assert0 So I'll need a bit more work. |
#133010 (comment) should help. It is good that you already have a reproducer. |
Ah, you have a different issue -- growing children, not attributes. The solution should be the same -- either ignore new items or resize the array in process. |
picnixz commentedMay 9, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Yes, actually I found a way to fix it in the meantime (but the same can be said) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
LGTM. 👍
root = ET.Element('a') | ||
evil = X('x') | ||
root.extend([evil, ET.Element('y')]) | ||
if is_python_implementation(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We could also make the C implementation raising RuntimeError. It is fine either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'll postpone this for a future PR as I want to backport this one to 3.13 and 3.14 first.
116a9f9
intopython:mainUh oh!
There was an error while loading.Please reload this page.
Thanks@picnixz for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14. |
…__` (pythonGH-133010)(cherry picked from commit116a9f9)Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
…__` (pythonGH-133010)(cherry picked from commit116a9f9)Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
GH-133805 is a backport of this pull request to the3.14 branch. |
GH-133806 is a backport of this pull request to the3.13 branch. |
Uh oh!
There was an error while loading.Please reload this page.
xml.etree.ElementTree.Element.__deepcopy__
when concurrent mutations happen #133009