Skip to content

Commit c5badac

Browse files
committed
avoid killing deepcopy() optimization
1 parent 7f8ed28 commit c5badac

File tree

1 file changed

+24
-22
lines changed

1 file changed

+24
-22
lines changed

Modules/_elementtree.c

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,6 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
805805
{
806806
Py_ssize_t i;
807807
ElementObject* element;
808-
PyObject* tmp;
809808
PyObject* tag;
810809
PyObject* attrib;
811810
PyObject* text;
@@ -814,16 +813,14 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
814813

815814
PyTypeObject *tp = Py_TYPE(self);
816815
elementtreestate *st = get_elementtree_state_by_type(tp);
817-
tmp = Py_NewRef(self->tag);
818-
tag = deepcopy(st, tmp, memo);
819-
Py_CLEAR(tmp);
816+
// The deepcopy() helper takes care of incrementing the refcount
817+
// of the object to copy so to avoid use-after-frees.
818+
tag = deepcopy(st, self->tag, memo);
820819
if (!tag)
821820
return NULL;
822821

823822
if (self->extra && self->extra->attrib) {
824-
tmp = Py_NewRef(self->extra->attrib);
825-
attrib = deepcopy(st, tmp, memo);
826-
Py_CLEAR(tmp);
823+
attrib = deepcopy(st, self->extra->attrib, memo);
827824
if (!attrib) {
828825
Py_DECREF(tag);
829826
return NULL;
@@ -840,16 +837,12 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
840837
if (!element)
841838
return NULL;
842839

843-
tmp = Py_NewRef(JOIN_OBJ(self->text));
844-
text = deepcopy(st, tmp, memo);
845-
Py_CLEAR(tmp);
840+
text = deepcopy(st, JOIN_OBJ(self->text), memo);
846841
if (!text)
847842
goto error;
848843
_set_joined_ptr(&element->text, JOIN_SET(text, JOIN_GET(self->text)));
849844

850-
tmp = Py_NewRef(JOIN_OBJ(self->tail));
851-
tail = deepcopy(st, tmp, memo);
852-
Py_CLEAR(tmp);
845+
tail = deepcopy(st, JOIN_OBJ(self->tail), memo);
853846
if (!tail)
854847
goto error;
855848
_set_joined_ptr(&element->tail, JOIN_SET(tail, JOIN_GET(self->tail)));
@@ -860,9 +853,7 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
860853
goto error;
861854

862855
for (i = 0; self->extra && i < self->extra->length; i++) {
863-
tmp = Py_NewRef(self->extra->children[i]);
864-
PyObject* child = deepcopy(st, tmp, memo);
865-
Py_CLEAR(tmp);
856+
PyObject* child = deepcopy(st, self->extra->children[i], memo);
866857
if (!child || !Element_Check(st, child)) {
867858
if (child) {
868859
raise_type_error(child);
@@ -877,7 +868,7 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
877868
assert(!element->extra->length);
878869
/*
879870
* The original 'self->extra' may be gone at this point if deepcopy()
880-
* had side-effects. However, 'i' is the number of copied items that
871+
* has side-effects. However, 'i' is the number of copied items that
881872
* we were able to successfully copy.
882873
*/
883874
element->extra->length = i;
@@ -914,6 +905,8 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
914905

915906
if (Py_REFCNT(object) == 1) {
916907
if (PyDict_CheckExact(object)) {
908+
// Exact dictionaries do not execute arbitrary code as it's
909+
// impossible to override their __iter__() method.
917910
PyObject *key, *value;
918911
Py_ssize_t pos = 0;
919912
int simple = 1;
@@ -923,13 +916,20 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
923916
break;
924917
}
925918
}
926-
if (simple)
919+
if (simple) {
920+
// This does not call object.__copy__(), so it's still safe.
927921
return PyDict_Copy(object);
922+
}
928923
/* Fall through to general case */
929924
}
930925
else if (Element_CheckExact(st, object)) {
931-
return _elementtree_Element___deepcopy___impl(
932-
(ElementObject *)object, memo);
926+
// The __deepcopy__() call may call arbitrary code even if the
927+
// object to copy is a built-in XML element (one of its children
928+
// may mutate 'object' in its own __deepcopy__() implementation).
929+
ElementObject *tmp = (ElementObject *)Py_NewRef(object);
930+
PyObject *res = _elementtree_Element___deepcopy___impl(tmp, memo);
931+
Py_DECREF(tmp);
932+
return res;
933933
}
934934
}
935935

@@ -940,8 +940,10 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
940940
return NULL;
941941
}
942942

943-
PyObject *args[2] = {object, memo};
944-
return PyObject_Vectorcall(st->deepcopy_obj, args, 2, NULL);
943+
PyObject *args[2] = {Py_NewRef(object), memo};
944+
PyObject *res = PyObject_Vectorcall(st->deepcopy_obj, args, 2, NULL);
945+
Py_DECREF(args[0]);
946+
return res;
945947
}
946948

947949

0 commit comments

Comments
 (0)