@@ -813,11 +813,16 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
813813
814814 PyTypeObject * tp = Py_TYPE (self );
815815 elementtreestate * st = get_elementtree_state_by_type (tp );
816+ // Since the 'tag' attribute is undeletable, deepcopy() should be safe.
816817 tag = deepcopy (st , self -> tag , memo );
818+ assert (self -> tag != NULL );
817819 if (!tag )
818820 return NULL ;
819821
820822 if (self -> extra && self -> extra -> attrib ) {
823+ // While deepcopy(self->extra->attrib) may cause 'self->extra' to be
824+ // NULL, we do not use it afterawrds without checking this, so no need
825+ // to temporarily incref 'self->extra->attrib'.
821826 attrib = deepcopy (st , self -> extra -> attrib , memo );
822827 if (!attrib ) {
823828 Py_DECREF (tag );
@@ -835,12 +840,16 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
835840 if (!element )
836841 return NULL ;
837842
843+ // Since the 'text' attribute is undeletable, deepcopy() should be safe.
838844 text = deepcopy (st , JOIN_OBJ (self -> text ), memo );
845+ assert (JOIN_OBJ (self -> text ) != NULL );
839846 if (!text )
840847 goto error ;
841848 _set_joined_ptr (& element -> text , JOIN_SET (text , JOIN_GET (self -> text )));
842849
850+ // Since the 'tail' attribute is undeletable, deepcopy() should be safe.
843851 tail = deepcopy (st , JOIN_OBJ (self -> tail ), memo );
852+ assert (JOIN_OBJ (self -> tail ) != NULL );
844853 if (!tail )
845854 goto error ;
846855 _set_joined_ptr (& element -> tail , JOIN_SET (tail , JOIN_GET (self -> tail )));
@@ -850,9 +859,11 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
850859 if (element_resize (element , self -> extra -> length ) < 0 )
851860 goto error ;
852861
853- // TODO(picnixz): check for an evil child's __deepcopy__ on 'self'
854- for (i = 0 ; i < self -> extra -> length ; i ++ ) {
855- PyObject * child = deepcopy (st , self -> extra -> children [i ], memo );
862+ // TODO(picnixz): should we protect against mutations from 'memo'?
863+ for (i = 0 ; self -> extra && i < self -> extra -> length ; i ++ ) {
864+ PyObject * itemi = Py_NewRef (self -> extra -> children [i ]);
865+ PyObject * child = deepcopy (st , itemi , memo );
866+ Py_DECREF (itemi );
856867 if (!child || !Element_Check (st , child )) {
857868 if (child ) {
858869 raise_type_error (child );
@@ -865,7 +876,12 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
865876 }
866877
867878 assert (!element -> extra -> length );
868- element -> extra -> length = self -> extra -> length ;
879+ /*
880+ * The original 'self->extra' may be gone at this point if deepcopy()
881+ * had side-effects. However, 'i' is the number of copied items that
882+ * we were able to successfully copy.
883+ */
884+ element -> extra -> length = i ;
869885 }
870886
871887 /* add object to memo dictionary (so deepcopy won't visit it again) */
0 commit comments